use of org.opengis.referencing.crs.CoordinateReferenceSystem in project sis by apache.
the class ImageFileDirectory method completeMetadata.
/**
* Completes the metadata with the information stored in the field of this IFD.
* This method is invoked only if the user requested the ISO 19115 metadata.
* This method creates a new {@code "metadata/contentInfo"} node for this image.
* Information not under the {@code "metadata/contentInfo"} node will be merged
* with the current content of the given {@code MetadataBuilder}.
*
* @param metadata where to write metadata information. Caller should have already invoked
* {@link MetadataBuilder#setFormat(String)} before {@code completeMetadata(…)} calls.
*/
final void completeMetadata(final MetadataBuilder metadata, final Locale locale) throws DataStoreContentException, FactoryException {
metadata.newCoverage(false);
if (compression != null) {
metadata.addCompression(compression.name().toLowerCase(locale));
}
for (int band = 0; band < samplesPerPixel; ) {
metadata.newSampleDimension();
metadata.setBitPerSample(bitsPerSample);
if (minValues != null)
metadata.addMinimumSampleValue(minValues.doubleValue(Math.min(band, minValues.size() - 1)));
if (maxValues != null)
metadata.addMaximumSampleValue(maxValues.doubleValue(Math.min(band, maxValues.size() - 1)));
metadata.setBandIdentifier(++band);
}
/*
* Add the resolution into the metadata. Our current ISO 19115 implementation restricts
* the resolution unit to metres, but it may be relaxed in a future SIS version.
*/
if (!Double.isNaN(resolution) && resolutionUnit != null) {
metadata.addResolution(resolutionUnit.getConverterTo(Units.METRE).convert(resolution));
}
/*
* Cell size is relevant only if the Threshholding TIFF tag value is 2. By convention in
* this implementation class, other Threshholding values are stored as negative cell sizes:
*
* -1 means that Threshholding is 1 or unspecified.
* -2 means that Threshholding is 2 but the matrix size has not yet been specified.
* -3 means that Threshholding is 3 (randomized process such as error diffusion).
*/
switch(Math.min(cellWidth, cellHeight)) {
case -1:
{
// Nothing to report.
break;
}
case -3:
{
metadata.addProcessDescription(Resources.formatInternational(Resources.Keys.RandomizedProcessApplied));
break;
}
default:
{
metadata.addProcessDescription(Resources.formatInternational(Resources.Keys.DitheringOrHalftoningApplied_2, (cellWidth >= 0) ? cellWidth : '?', (cellHeight >= 0) ? cellHeight : '?'));
break;
}
}
/*
* Add Coordinate Reference System built from GeoTIFF tags. Note that the CRS may not exist,
* in which case the CRS builder returns null. This is safe since all MetadataBuilder methods
* ignore null values (a design choice because this pattern come very often).
*/
final boolean isGeorectified = (modelTiePoints == null) || (gridToCRS != null);
metadata.newGridRepresentation(isGeorectified ? MetadataBuilder.GridType.GEORECTIFIED : MetadataBuilder.GridType.GEOREFERENCEABLE);
metadata.setGeoreferencingAvailability(gridToCRS != null, false, false);
CoordinateReferenceSystem crs = null;
if (geoKeyDirectory != null) {
final CRSBuilder helper = new CRSBuilder(reader);
try {
crs = helper.build(geoKeyDirectory, numericGeoParameters, asciiGeoParameters);
metadata.addReferenceSystem(crs);
helper.complete(metadata);
} catch (NoSuchIdentifierException | ParameterNotFoundException e) {
short key = Resources.Keys.UnsupportedProjectionMethod_1;
if (e instanceof NoSuchAuthorityCodeException) {
key = Resources.Keys.UnknownCRS_1;
}
reader.owner.warning(reader.resources().getString(key, reader.owner.getDisplayName()), e);
} catch (IllegalArgumentException | NoSuchElementException | ClassCastException e) {
if (!helper.alreadyReported) {
reader.owner.warning(null, e);
}
}
}
try {
if (!isGeorectified) {
metadata.addGeolocation(new GridGeometry(filename(), crs, modelTiePoints));
}
} catch (TransformException e) {
reader.owner.warning(null, e);
}
// Not needed anymore, so let GC do its work.
geoKeyDirectory = null;
numericGeoParameters = null;
asciiGeoParameters = null;
modelTiePoints = null;
}
use of org.opengis.referencing.crs.CoordinateReferenceSystem in project sis by apache.
the class TransformCommand method run.
/**
* Transforms coordinates from the files given in argument or from the standard input stream.
*
* @return 0 on success, or an exit code if the command failed for a reason other than an uncaught Java exception.
*/
@Override
public int run() throws Exception {
final CoordinateReferenceSystem sourceCRS = fetchCRS(Option.SOURCE_CRS);
final CoordinateReferenceSystem targetCRS = fetchCRS(Option.TARGET_CRS);
/*
* Read all coordinates, so we can compute the area of interest.
* This will be used when searching for a coordinate operation.
*/
GeographicBoundingBox areaOfInterest = null;
List<double[]> points = Collections.emptyList();
final boolean useStandardInput = useStandardInput();
if (useStandardInput || !files.isEmpty()) {
if (useStandardInput) {
try (LineNumberReader in = new LineNumberReader(new InputStreamReader(System.in, encoding))) {
points = readCoordinates(in, "stdin");
}
} else {
for (final String file : files) {
try (LineNumberReader in = new LineNumberReader(new InputStreamReader(new FileInputStream(file), encoding))) {
points = readCoordinates(in, file);
}
}
}
try {
final GeographicCRS domainOfValidityCRS = ReferencingUtilities.toNormalizedGeographicCRS(sourceCRS);
if (domainOfValidityCRS != null) {
toDomainOfValidity = CRS.findOperation(sourceCRS, domainOfValidityCRS, null).getMathTransform();
areaOfInterest = computeAreaOfInterest(points);
}
} catch (FactoryException e) {
warning(e);
}
}
operation = CRS.findOperation(sourceCRS, targetCRS, areaOfInterest);
/*
* Prints the header: source CRS, target CRS, operation steps and positional accuracy.
*/
outHeader = new TableAppender(new LineAppender(out), " ");
outHeader.setMultiLinesCells(true);
printHeader(Vocabulary.Keys.Source);
printNameAndIdentifier(operation.getSourceCRS(), false);
printHeader(Vocabulary.Keys.Destination);
printNameAndIdentifier(operation.getTargetCRS(), false);
printHeader(Vocabulary.Keys.Operations);
printOperations(operation, false);
outHeader.nextLine();
printDomainOfValidity(operation.getDomainOfValidity());
printAccuracy(CRS.getLinearAccuracy(operation));
if (options.containsKey(Option.VERBOSE)) {
printDetails();
}
outHeader.flush();
outHeader = null;
/*
* At this point we finished to write the header. If there is at least one input file,
* compute the number of digits to format and perform the actual coordinate operations.
*/
if (!points.isEmpty()) {
// Must be set before computeNumFractionDigits(…).
ordinateWidth = 15;
coordinateFormat = NumberFormat.getInstance(Locale.US);
coordinateFormat.setGroupingUsed(false);
computeNumFractionDigits(operation.getTargetCRS().getCoordinateSystem());
out.println();
printAxes(operation.getTargetCRS().getCoordinateSystem());
out.println();
transform(points);
if (errorMessage != null) {
error(errorMessage, errorCause);
}
}
return 0;
}
use of org.opengis.referencing.crs.CoordinateReferenceSystem in project sis by apache.
the class CRS method suggestCommonTarget.
/**
* Suggests a coordinate reference system which could be a common target for coordinate operations having the
* given sources. This method compares the {@linkplain #getGeographicBoundingBox(CoordinateReferenceSystem)
* domain of validity} of all given CRSs. If a CRS has a domain of validity that contains the domain of all other
* CRS, than that CRS is returned. Otherwise this method verifies if a {@linkplain GeneralDerivedCRS#getBaseCRS()
* base CRS} (usually a {@linkplain org.apache.sis.referencing.crs.DefaultGeographicCRS geographic CRS} instance)
* would be suitable. If no suitable CRS is found, then this method returns {@code null}.
*
* <div class="note"><b>Use case:</b>
* before to test if two arbitrary envelopes {@linkplain GeneralEnvelope#intersects(Envelope) intersect} each other,
* they need to be {@linkplain Envelopes#transform(Envelope, CoordinateReferenceSystem) transformed} in the same CRS.
* However if one CRS is a Transverse Mercator projection while the other CRS is a world-wide geographic CRS, then
* attempts to use the Transverse Mercator projection as the common CRS is likely to fail since the geographic envelope
* may span an area far outside the projection domain of validity. This {@code suggestCommonTarget(…)} method can used
* for choosing a common CRS which is less likely to fail.</div>
*
* @param regionOfInterest the geographic area for which the coordinate operations will be applied,
* or {@code null} if unknown.
* @param sourceCRS the coordinate reference systems for which a common target CRS is desired.
* @return a CRS that may be used as a common target for all the given source CRS in the given region of interest,
* or {@code null} if this method did not find a common target CRS. The returned CRS may be different than
* all given CRS.
*
* @since 0.8
*/
public static CoordinateReferenceSystem suggestCommonTarget(GeographicBoundingBox regionOfInterest, CoordinateReferenceSystem... sourceCRS) {
CoordinateReferenceSystem bestCRS = null;
/*
* Compute the union of the domain of validity of all CRS. If a CRS does not specify a domain of validity,
* then assume that the CRS is valid for the whole world if the CRS is geodetic or return null otherwise.
* Opportunistically remember the domain of validity of each CRS in this loop since we will need them later.
*/
boolean worldwide = false;
DefaultGeographicBoundingBox domain = null;
final GeographicBoundingBox[] domains = new GeographicBoundingBox[sourceCRS.length];
for (int i = 0; i < sourceCRS.length; i++) {
final CoordinateReferenceSystem crs = sourceCRS[i];
final GeographicBoundingBox bbox = getGeographicBoundingBox(crs);
if (bbox == null) {
/*
* If no domain of validity is specified and we can not fallback
* on some knowledge about what the CRS is, abandon.
*/
if (!(crs instanceof GeodeticCRS)) {
return null;
}
/*
* Geodetic CRS (geographic or geocentric) can generally be presumed valid in a worldwide area.
* The 'worldwide' flag is a little optimization for remembering that we do not need to compute
* the union anymore, but we still need to continue the loop for fetching all bounding boxes.
*/
// Fallback to be used if we don't find anything better.
bestCRS = crs;
worldwide = true;
} else {
domains[i] = bbox;
if (!worldwide) {
if (domain == null) {
domain = new DefaultGeographicBoundingBox(bbox);
} else {
domain.add(bbox);
}
}
}
}
/*
* At this point we got the union of the domain of validity of all CRS. We are interested only in the
* part that intersect the region of interest. If the union is whole world, we do not need to compute
* the intersection; we can just leave the region of interest unchanged.
*/
if (domain != null && !worldwide) {
if (regionOfInterest != null) {
domain.intersect(regionOfInterest);
}
regionOfInterest = domain;
domain = null;
}
/*
* Iterate again over the domain of validity of all CRS. For each domain of validity, compute the area
* which is inside the domain or interest and the area which is outside. The "best CRS" will be the one
* which comply with the following rules, in preference order:
*
* 1) The CRS which is valid over the largest area of the region of interest.
* 2) If two CRS are equally good according rule 1, then the CRS with the smallest "outside area".
*
* Example: given two source CRS, a geographic one and a projected one:
*
* - If the projected CRS contains fully the region of interest, then it will be returned.
* The preference is given to the projected CRS because geometric are likely to be more
* accurate in that space. Furthermore forward conversions from geographic to projected
* CRS are usually faster than inverse conversions.
*
* - Otherwise (i.e. if the region of interest is likely to be wider than the projected CRS
* domain of validity), then the geographic CRS will be returned.
*/
// NaN if 'regionOfInterest' is null.
final double roiArea = Extents.area(regionOfInterest);
double maxInsideArea = 0;
double minOutsideArea = Double.POSITIVE_INFINITY;
boolean tryDerivedCRS = false;
do {
for (int i = 0; i < domains.length; i++) {
final GeographicBoundingBox bbox = domains[i];
if (bbox != null) {
double insideArea = Extents.area(bbox);
double outsideArea = 0;
if (regionOfInterest != null) {
if (domain == null) {
domain = new DefaultGeographicBoundingBox(bbox);
} else {
domain.setBounds(bbox);
}
domain.intersect(regionOfInterest);
final double area = insideArea;
insideArea = Extents.area(domain);
outsideArea = area - insideArea;
}
if (insideArea > maxInsideArea || (insideArea == maxInsideArea && outsideArea < minOutsideArea)) {
maxInsideArea = insideArea;
minOutsideArea = outsideArea;
bestCRS = sourceCRS[i];
}
}
}
/*
* If the best CRS does not cover fully the region of interest, then we will redo the check again
* but using base CRS instead. For example if the list of source CRS had some projected CRS, we
* will try with the geographic CRS on which those projected CRS are based.
*/
if (maxInsideArea < roiArea) {
// Do not try twice.
if (tryDerivedCRS)
break;
final CoordinateReferenceSystem[] derivedCRS = new CoordinateReferenceSystem[sourceCRS.length];
for (int i = 0; i < derivedCRS.length; i++) {
GeographicBoundingBox bbox = null;
final CoordinateReferenceSystem crs = sourceCRS[i];
if (crs instanceof GeneralDerivedCRS) {
final CoordinateReferenceSystem baseCRS = ((GeneralDerivedCRS) crs).getBaseCRS();
bbox = getGeographicBoundingBox(baseCRS);
if (bbox == null && bestCRS == null && baseCRS instanceof GeodeticCRS) {
// Fallback to be used if we don't find anything better.
bestCRS = baseCRS;
}
tryDerivedCRS = true;
derivedCRS[i] = baseCRS;
}
domains[i] = bbox;
}
sourceCRS = derivedCRS;
} else {
break;
}
} while (tryDerivedCRS);
return bestCRS;
}
use of org.opengis.referencing.crs.CoordinateReferenceSystem in project sis by apache.
the class DefaultCompoundCRS method verify.
/**
* Verifies that the given array does not contain duplicated horizontal or vertical components.
* Verifies also that if there is an horizontal component, then there is no ellipsoidal height
* defined separately.
*
* @param properties the user-specified properties, for determining the locale of error messages.
* @param components the components to verify.
*/
private static void verify(final Map<String, ?> properties, final CoordinateReferenceSystem[] components) {
int allTypes = 0;
// 0 for false, 1 for true.
int isProjected = 0;
boolean isEllipsoidalHeight = false;
for (final CoordinateReferenceSystem component : components) {
final int type;
if (component instanceof GeodeticCRS) {
// Must match the number used in Resources.Keys.DuplicatedSpatialComponents_1.
type = 1;
} else if (component instanceof ProjectedCRS) {
isProjected = 1;
// Intentionally same number than for GeographicCRS case.
type = 1;
} else if (component instanceof VerticalCRS) {
isEllipsoidalHeight = ReferencingUtilities.isEllipsoidalHeight(((VerticalCRS) component).getDatum());
// Must match the number used in Resources.Keys.DuplicatedSpatialComponents_1.
type = 2;
} else {
// Skip other types. In particular, we allow 2 temporal CRS (used in meteorology).
continue;
}
if (allTypes == (allTypes |= type)) {
throw new IllegalArgumentException(Resources.forProperties(properties).getString(Resources.Keys.DuplicatedSpatialComponents_1, type));
}
}
if (isEllipsoidalHeight && ((allTypes & 1) != 0)) {
throw new IllegalArgumentException(Resources.forProperties(properties).getString(Resources.Keys.EllipsoidalHeightNotAllowed_1, isProjected));
}
}
use of org.opengis.referencing.crs.CoordinateReferenceSystem in project sis by apache.
the class DefaultCompoundCRS method createCoordinateSystem.
/**
* Returns a compound coordinate system for the specified array of CRS objects.
*
* @param properties the properties given to the constructor, or {@code null} if unknown.
* @param components the CRS components, usually singles but not necessarily.
* @return the coordinate system for the given components.
*/
private static CoordinateSystem createCoordinateSystem(final Map<String, ?> properties, final CoordinateReferenceSystem[] components) {
ArgumentChecks.ensureNonNull("components", components);
verify(properties, components);
if (components.length < 2) {
throw new IllegalArgumentException(Errors.getResources(properties).getString(Errors.Keys.TooFewArguments_2, 2, components.length));
}
final CoordinateSystem[] cs = new CoordinateSystem[components.length];
for (int i = 0; i < components.length; i++) {
final CoordinateReferenceSystem crs = components[i];
ArgumentChecks.ensureNonNullElement("components", i, crs);
cs[i] = crs.getCoordinateSystem();
}
return new DefaultCompoundCS(cs);
}
Aggregations