use of org.geotoolkit.image.io.metadata.ReferencingBuilder in project geotoolkit by Geomatys.
the class WorldFileImageReader method createMetadata.
/**
* Creates a new stream or image metadata. This method first delegates to the main reader as
* documented in the {@linkplain ImageReaderAdapter#createMetadata(int) super-class method},
* then completes the metadata with information read from the <cite>World File</cite> and
* <cite>Map Projection</cite> files.
* <p>
* The <cite>World File</cite> and <cite>Map Projection</cite> files are determined by calls
* to the {@link #createInput(String)} method with {@code "tfw"} and {@code "prj"} argument
* values. Subclasses can override the later method if they want to specify different files
* to be read.
*/
@Override
protected SpatialMetadata createMetadata(final int imageIndex) throws IOException {
SpatialMetadata metadata = super.createMetadata(imageIndex);
if (imageIndex >= 0) {
AffineTransform gridToCRS = null;
CoordinateReferenceSystem crs = null;
Object in = getVerifiedInput("tfw");
if (in != null) {
gridToCRS = SupportFiles.parseTFW(IOUtilities.open(in), in);
}
in = getVerifiedInput("prj");
if (in != null) {
crs = PrjFiles.read(IOUtilities.open(in), true);
}
/*
* If we have found information in TFW or PRJ files, complete metadata.
*/
if (gridToCRS != null || crs != null) {
// -- if exist some metadata from sub reader complete them, else create new spatial metadata
if (main instanceof SpatialImageReader) {
metadata = ((SpatialImageReader) main).getImageMetadata(imageIndex);
} else {
metadata = new SpatialMetadata(false, this, null);
}
if (gridToCRS != null) {
final int width = getWidth(imageIndex);
final int height = getHeight(imageIndex);
new GridDomainAccessor(metadata).setAll(gridToCRS, new Rectangle(width, height), null, PixelOrientation.UPPER_LEFT);
}
if (crs != null) {
new ReferencingBuilder(metadata).setCoordinateReferenceSystem(crs);
}
}
}
return metadata;
}
use of org.geotoolkit.image.io.metadata.ReferencingBuilder in project geotoolkit by Geomatys.
the class TextImageWriterTestBase method createMetadata.
/**
* Creates dummy metadata for the image to be returned by {@link #createImage()}.
*/
private static IIOMetadata createMetadata() {
final IIOMetadata metadata = new SpatialMetadata(SpatialMetadataFormat.getImageInstance(GEOTK_FORMAT_NAME));
final GridDomainAccessor domain = new GridDomainAccessor(metadata);
domain.setOrigin(-500, 400);
domain.addOffsetVector(100, 0);
domain.addOffsetVector(0, -100);
final DimensionAccessor dimensions = new DimensionAccessor(metadata);
dimensions.selectChild(dimensions.appendChild());
dimensions.setValueRange(0f, 88.97f);
// Intentionnaly use a value different than -9999.
dimensions.setFillSampleValues(-9998);
/*
* Adds a Coordinate Reference System.
* We use a simple Mercator projection.
*/
try {
new ReferencingBuilder(metadata).setCoordinateReferenceSystem(CRS.fromWKT(WKT.PROJCS_MERCATOR));
} catch (FactoryException e) {
fail(e.toString());
}
return metadata;
}
use of org.geotoolkit.image.io.metadata.ReferencingBuilder in project geotoolkit by Geomatys.
the class ImageCoverageWriter method write.
/**
* Writes a single coverage, which may be an element of a sequence. This method needs to be
* informed when it is writing the first or the last coverage of a sequence. If there is only
* one coverage to write, than both {@code isFirst} and {@code isLast} must be {@code true}.
* <p>
* In current implementation, the stream metadata are generated from the first image only
* (when {@code isFirst == true}) and the log message (if any) shows the grid geometry of
* the last coverage only (when {@code isLast == true}). It should not be an issue in the
* common case where all coverage in the sequence have similar grid geometry or metadata.
*
* @param coverages The coverages to write.
* @param param Optional parameters used to control the writing process, or {@code null}.
* @param isFirst {@code true} if writing the first coverage of a sequence.
* @param isLast {@code true} if writing the last coverage of a sequence.
* @param startTime Nano time when the writing process started, or {@link Long#MIN_VALUE}
* if the operation duration is not logged.
* @throws IllegalStateException If the output destination has not been set.
* @throws CoverageStoreException If the iterable contains an unsupported number of coverages,
* or if an error occurs while writing the information to the output destination.
* @throws CancellationException If {@link #abort()} has been invoked in an other thread during
* the execution of this method.
*
* @since 3.20
*/
private void write(final GridCoverage coverage, final GridCoverageWriteParam param, final boolean isFirst, final boolean isLast, final long startTime) throws DataStoreException, CancellationException {
/*
* Prepares an initially empty ImageWriteParam, to be filled later with the values
* provided in the GridCoverageWriteParam. In order to get the ImageWriteParam, we
* need the ImageWriter, which need the RenderedImage, which need the GridGeometry.
*/
GridGeometry gridGeometry = coverage.getGridGeometry().reduce(0, 1);
RenderedImage image = coverage.render(null);
while (image instanceof RenderedImageAdapter) {
image = ((RenderedImageAdapter) image).getWrappedImage();
}
if (isFirst) {
final String imageFormat = (param != null) ? param.getFormatName() : null;
setImageOutput(image, imageFormat);
}
/*
* The ImageWriter is created by the call to setImageOutput.
* We can verify its validity only at this point.
*/
// Protect from changes.
final ImageWriter imageWriter = this.imageWriter;
if (imageWriter == null) {
throw new IllegalStateException(formatErrorMessage(Errors.Keys.NoImageOutput));
}
if (!isLast && !imageWriter.canWriteSequence()) {
throw new CoverageStoreException(Errors.format(Errors.Keys.UnsupportedMultiOccurrence_1, GridCoverage.class));
}
// TODO: DEPRECATED: to be removed in Apache SIS.
final boolean isNetcdfHack = imageWriter.getClass().getName().equals("org.geotoolkit.image.io.plugin.NetcdfImageWriter");
final boolean isTiffHack = imageWriter.getClass().getName().equals("org.geotoolkit.image.io.plugin.TiffImageWriter");
/*
* Convert the geodetic coordinates to pixel coordinates.
*/
final ImageWriteParam imageParam;
try {
imageParam = createImageWriteParam(image);
} catch (IOException e) {
throw new CoverageStoreException(formatErrorMessage(e), e);
}
MathTransform2D destToExtractedGrid = null;
PlanarImage toDispose = null;
if (param != null) {
/*
* Now convert the GridCoverageWriteParam values to ImageWriteParam value.
* First of all, convert the ISO 119123 InterpolationMethod code to the JAI
* code.
*/
final int interp;
final InterpolationMethod interpolation = param.getInterpolation();
if (interpolation.equals(InterpolationMethod.NEAREST_NEIGHBOUR)) {
interp = Interpolation.INTERP_NEAREST;
} else if (interpolation.equals(InterpolationMethod.BILINEAR)) {
interp = Interpolation.INTERP_BILINEAR;
} else if (interpolation.equals(InterpolationMethod.BICUBIC)) {
interp = Interpolation.INTERP_BICUBIC;
} else {
throw new CoverageStoreException(Errors.getResources(locale).getString(Errors.Keys.IllegalArgument_2, "interpolation", interpolation.name()));
}
destToExtractedGrid = geodeticToPixelCoordinates(gridGeometry, param, imageParam, isNetcdfHack);
imageParam.setSourceBands(param.getSourceBands());
final Rectangle sourceRegion = imageParam.getSourceRegion();
final Rectangle requestRegion = requestedBounds;
if (interp != Interpolation.INTERP_NEAREST || !isIdentity(destToExtractedGrid) || isGreater(requestRegion.width, imageParam.getSourceXSubsampling(), sourceRegion.width) || isGreater(requestRegion.height, imageParam.getSourceYSubsampling(), sourceRegion.height)) {
/*
* We need to resample the image if:
*
* - The transform from the source grid to the target grid is not affine;
* - The above transform is affine but more complex than scale and translations;
* - The translation or scale factors of the above transform are not integers;
* - The requested envelope is greater than the coverage envelope;
*/
final InternationalString name = null;
final ImageLayout layout = new ImageLayout(requestRegion.x, requestRegion.y, requestRegion.width, requestRegion.height);
/*
* Some codecs (e.g. JPEG) require that the whole image is available
* as a single raster.
*/
layout.setTileWidth(requestRegion.width);
layout.setTileHeight(requestRegion.height);
final RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout);
// Will be used for logging purpose.
destToExtractedGrid = (MathTransform2D) destGridToSource;
final Warp warp;
try {
warp = WarpFactory.DEFAULT.create(name, destToExtractedGrid, sourceRegion);
} catch (TransformException e) {
throw new CoverageStoreException(formatErrorMessage(e), e);
}
if (DEBUG) {
/*
* To be enabled only when debugging.
* Simplified output example from the writeSubsampledRegion() test:
*
* Grid to source: ┌ ┐
* │ 2 0 9 │
* │ 0 3 9 │
* │ 0 0 1 │
* └ ┘
* Source region: Rectangle[x=9, y=9, width=9, height=15]
* Warp origin: [9.5, 10.0]
*
* If we had no scale factor, the Warp origin would be the same than the
* translations. If we have scale factors be were mapping pixel corners,
* then the warp origin would also be the same.
*
* But the JAI Warp operation maps pixel center. It does so by adding 0.5
* to pixel coordinates before applying the Warp, and removing 0.5 to the
* result (see WarpTransform2D.getWarp(...) javadoc). This is actually the
* desired behavior, as we can see with the picture below which represents
* only the first pixel of the destination image. The cell are the source
* pixels, the transform is the above matrix, and the coordinates are
* relative to the source grid:
*
* (9,9)
* ┌─────┬─────┐
* │ │ │
* ├─────┼─────┤
* │ (10,10.5) │ after the -0.5 final offset, become (9.5, 10).
* ├─────┼─────┤
* │ │ │
* └─────┴─────┘
* (11,12)
*/
Object tr = destToExtractedGrid;
if (tr instanceof LinearTransform) {
tr = ((LinearTransform) tr).getMatrix();
}
final TableAppender table = new TableAppender(" ");
table.setMultiLinesCells(true);
table.appendHorizontalSeparator();
table.append("Warping coverage:");
table.nextColumn();
table.append(String.valueOf(name));
table.nextLine();
table.append("Grid to source:");
table.nextColumn();
table.append(String.valueOf(tr));
table.nextLine();
table.append("Source region:");
table.nextColumn();
table.append(String.valueOf(sourceRegion));
table.nextLine();
table.append("Warp origin:");
table.nextColumn();
table.append(Arrays.toString(warp.warpPoint(0, 0, null)));
table.nextLine();
table.appendHorizontalSeparator();
System.out.println(table);
}
double[] backgroundValues = param.getBackgroundValues();
if (backgroundValues == null) {
backgroundValues = CoverageUtilities.getBackgroundValues(coverage);
}
image = toDispose = WarpDescriptor.create(image, warp, Interpolation.getInstance(interp), backgroundValues, hints);
imageParam.setSourceRegion(null);
imageParam.setSourceSubsampling(1, 1, 0, 0);
}
/*
* Set other parameters inferred from the GridCoverageWriteParam.
*/
if (imageParam.canWriteCompressed()) {
final Float compression = param.getCompressionQuality();
if (compression != null) {
imageParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
imageParam.setCompressionQuality(compression);
}
}
}
if (imageParam.canWriteTiles()) {
imageParam.setTilingMode(ImageWriteParam.MODE_EXPLICIT);
// -- one destination tile equals source image tile representation
imageParam.setTiling(image.getTileWidth() / imageParam.getSourceXSubsampling(), image.getTileHeight() / imageParam.getSourceYSubsampling(), 0, 0);
}
/*
* Creates metadata with the information calculated so far. The code above this
* point should have created an image having a grid geometry matching the user
* request, so we will write that user request in the metadata.
*/
final ImageTypeSpecifier imageType = ImageTypeSpecifier.createFromRenderedImage(image);
final IIOMetadata streamMetadata = isFirst ? imageWriter.getDefaultStreamMetadata(imageParam) : null;
final IIOMetadata imageMetadata = imageWriter.getDefaultImageMetadata(imageType, imageParam);
if (imageMetadata != null && ArraysExt.contains(imageMetadata.getMetadataFormatNames(), GEOTK_FORMAT_NAME)) {
CoordinateReferenceSystem crs = null;
Envelope env = null;
double[] res = null;
if (param != null) {
crs = param.getCoordinateReferenceSystem();
env = param.getEnvelope();
res = param.getResolution();
}
if (crs == null && gridGeometry.isDefined(GridGeometry.CRS)) {
crs = gridGeometry.getCoordinateReferenceSystem();
}
if (env == null && gridGeometry.isDefined(GridGeometry.ENVELOPE)) {
env = gridGeometry.getEnvelope();
}
if (crs != null) {
final ReferencingBuilder builder = new ReferencingBuilder(imageMetadata);
builder.setCoordinateReferenceSystem(crs);
}
if (env != null) {
final GridDomainAccessor accessor = new GridDomainAccessor(imageMetadata);
final Dimension size = getImageSize(image, imageParam);
final double ymax = env.getMaximum(Y_DIMENSION);
final double[] origin = env.getLowerCorner().getCoordinate();
final int dim = origin.length;
origin[Y_DIMENSION] = ymax;
if (res != null) {
accessor.setOrigin(origin);
final double[] p = new double[dim];
final double[] median = new double[dim];
for (int i = 0; i < dim; i++) {
Arrays.fill(p, 0);
if (i == X_DIMENSION) {
p[i] = +res[X_DIMENSION];
} else if (i == Y_DIMENSION) {
p[i] = -res[Y_DIMENSION];
} else {
p[i] = 1;
}
accessor.addOffsetVector(p);
median[i] = env.getMedian(i);
}
accessor.setSpatialRepresentation(median, null, PixelOrientation.UPPER_LEFT);
final int[] maxGrid = new int[dim];
Arrays.fill(maxGrid, 1);
maxGrid[X_DIMENSION] = size.width - 1;
maxGrid[Y_DIMENSION] = size.height - 1;
accessor.setLimits(new int[dim], maxGrid);
} else {
final double[] envBounds = env.getUpperCorner().getCoordinate();
envBounds[Y_DIMENSION] = env.getMinimum(Y_DIMENSION);
final int[] high = new int[dim];
Arrays.fill(high, 1);
high[X_DIMENSION] = size.width - 1;
high[Y_DIMENSION] = size.height - 1;
accessor.setRectifiedGridDomain(origin, envBounds, null, high, null, false);
accessor.setSpatialRepresentation(origin, envBounds, null, PixelOrientation.UPPER_LEFT);
}
}
final List<SampleDimension> dims = coverage.getSampleDimensions();
final DimensionAccessor accessor = new DimensionAccessor(imageMetadata);
for (int i = 0, n = dims.size(); i < n; i++) {
final SampleDimension band = dims.get(i);
accessor.selectChild(accessor.appendChild());
if (band != null) {
accessor.setDimension(band, locale);
}
}
}
/*
* Now process to the coverage writing. If the coverage is the only image (i.e. is both
* the first and the last image), then we will write everything in a single operation by
* a call to ImageWriter.write(...). Otherwise we will need to use the
* prepareWriteSequence() - writeSequence(...) - endWriteSequence() cycle.
*/
checkAbortState();
try {
if (streamMetadata != null) {
completeImageMetadata(streamMetadata, null);
}
completeImageMetadata(imageMetadata, coverage);
final IIOImage iio = new IIOImage(image, null, imageMetadata);
if (isFirst & isLast) {
imageWriter.write(streamMetadata, iio, imageParam);
} else {
if (isFirst) {
imageWriter.prepareWriteSequence(streamMetadata);
}
imageWriter.writeToSequence(iio, imageParam);
if (isLast) {
imageWriter.endWriteSequence();
}
}
} catch (IOException e) {
throw new CoverageStoreException(formatErrorMessage(e), e);
}
checkAbortState();
/*
* Finally, logs the operation after the last image if logging are enabled.
* The log level will depend on how long it took to write every images in
* the sequence.
*/
if (isLast && startTime != Long.MIN_VALUE) {
final long time = System.nanoTime() - startTime;
final Level level = getLogLevel(time);
if (LOGGER.isLoggable(level)) {
final Dimension size = getImageSize(image, imageParam);
CoordinateReferenceSystem crs = null;
if (param != null) {
crs = param.getCoordinateReferenceSystem();
}
ImageCoverageStore.logOperation(level, locale, ImageCoverageWriter.class, true, output, 0, coverage, size, crs, destToExtractedGrid, time);
}
}
if (toDispose != null) {
toDispose.dispose();
}
}
use of org.geotoolkit.image.io.metadata.ReferencingBuilder in project geotoolkit by Geomatys.
the class GeoTiffCRSReader method fillProjectedCRSMetaDatas.
/**
* Fill a projected CRS metadatas with the values available in the geotiff tags.
*/
private void fillProjectedCRSMetaDatas(final SpatialMetadata metadatas, final ValueMap entries) throws IOException, FactoryException {
final ReferencingBuilder rb = new ReferencingBuilder(metadatas);
final CoordinateReferenceSystem crs;
// //
// Get the projection reference system code in case we have one by
// lookig for the ProjectedCSTypeGeoKey key
// //
String tempCode = entries.getAsString(ProjectedCSTypeGeoKey);
if (tempCode == null) {
tempCode = "unnamed";
}
final StringBuffer projCode = new StringBuffer(tempCode.trim().intern());
// //
// getting the linear unit used by this coordinate reference system
// since we will use it anyway.
// //
Unit linearUnit;
try {
linearUnit = createUnit(ProjLinearUnitsGeoKey, ProjLinearUnitSizeGeoKey, Units.METRE, Units.METRE, entries);
} catch (IOException e) {
linearUnit = null;
}
// //
if (tempCode.equalsIgnoreCase("unnamed") || tempCode.equals(GTUserDefinedGeoKey_String)) {
crs = createUserDefinedPCS(entries, linearUnit);
} else {
// //
try {
if (!tempCode.startsWith("EPSG") && !tempCode.startsWith("epsg")) {
projCode.insert(0, "EPSG:");
}
// it is an EPSG crs let's create it.
// TODO : jsorel : are we sure of this ? always long/lat order ?
final ProjectedCRS pcrs = (ProjectedCRS) AbstractCRS.castOrCopy(CRS.forCode(projCode.toString())).forConvention(AxesConvention.RIGHT_HANDED);
// //
if (linearUnit == null || linearUnit.equals(pcrs.getCoordinateSystem().getAxis(0).getUnit())) {
crs = pcrs;
} else {
// //
// Creating anew projected CRS
// //
crs = new DefaultProjectedCRS(java.util.Collections.singletonMap("name", IdentifiedObjects.getName(pcrs, new DefaultCitation("EPSG"))), (GeographicCRS) pcrs.getBaseCRS(), pcrs.getConversionFromBase(), createProjectedCS(linearUnit));
}
} catch (FactoryException fe) {
throw new IOException(fe);
}
}
rb.setCoordinateReferenceSystem(crs);
}
use of org.geotoolkit.image.io.metadata.ReferencingBuilder in project geotoolkit by Geomatys.
the class GeoTiffCRSReader method fillGeographicCRSMetaDatas.
/**
* Fill a geographic CRS metadatas with the values available in the geotiff tags.
*/
private void fillGeographicCRSMetaDatas(final SpatialMetadata metadatas, final ValueMap entries) throws IOException, FactoryException {
GeographicCRS gcs = null;
// ////////////////////////////////////////////////////////////////////
// Get the crs code
// ////////////////////////////////////////////////////////////////////
final String tempCode = entries.getAsString(GeographicTypeGeoKey);
// lookup the angular units used in this geotiff image
Unit angularUnit = null;
try {
angularUnit = createUnit(GeogAngularUnitsGeoKey, GeogAngularUnitSizeGeoKey, Units.RADIAN, Units.DEGREE, entries);
} catch (IOException e) {
angularUnit = null;
}
// linear unit
Unit linearUnit = null;
try {
linearUnit = createUnit(GeogLinearUnitsGeoKey, GeogLinearUnitSizeGeoKey, Units.METRE, Units.METRE, entries);
} catch (IOException e) {
linearUnit = null;
}
// if it's user defined, there's a lot of work to do
if (tempCode == null || tempCode.equals(GeoTiffConstants.GTUserDefinedGeoKey_String)) {
// ////////////////////////////////////////////////////////////////////
// it is user-defined we have to parse a lot of information in order
// to built it.
// ////////////////////////////////////////////////////////////////////
gcs = createUserDefinedGCS(entries, linearUnit, angularUnit);
} else {
try {
// ////////////////////////////////////////////////////////////////////
// If it's not user defined, just use the EPSG factory to create
// the coordinate system but check if the user specified a
// different angular unit. In this case we need to create a
// user-defined GCRS.
// ////////////////////////////////////////////////////////////////////
final StringBuffer geogCode = new StringBuffer(tempCode);
if (!tempCode.startsWith("EPSG") && !tempCode.startsWith("epsg")) {
geogCode.insert(0, "EPSG:");
}
// TODO : jsorel : are we sure of this ? always long/lat order ?
gcs = (GeographicCRS) AbstractCRS.castOrCopy(CRS.forCode(geogCode.toString())).forConvention(AxesConvention.RIGHT_HANDED);
if (angularUnit != null && !angularUnit.equals(gcs.getCoordinateSystem().getAxis(0).getUnit())) {
// //
// Create a user-defined GCRS using the provided angular
// unit.
// //
gcs = new DefaultGeographicCRS(name(IdentifiedObjects.getName(gcs, new DefaultCitation("EPSG"))), (GeodeticDatum) gcs.getDatum(), PredefinedCS.usingUnit(CommonCRS.defaultGeographic().getCoordinateSystem(), angularUnit));
}
} catch (FactoryException ex) {
throw new IOException(ex);
}
}
ReferencingBuilder rb = new ReferencingBuilder(metadatas);
rb.setCoordinateReferenceSystem(gcs);
}
Aggregations