use of org.opengis.coverage.grid.RectifiedGrid in project geotoolkit by Geomatys.
the class GeoTiffExtension method setOrCreateSliceDimension.
/**
* Modify the given spatial metadata, adding a new crs axis dimension.
* If the crs axis already exist it will not be added and only the value will be updated.
*
* This method should be used only to declare the geotiff on a new axis.
* For example adding a temporal dimension and define it's value.
*
* @param metadata
* @param axisCrs
* @param value
* @throws FactoryException
*/
public static void setOrCreateSliceDimension(SpatialMetadata metadata, CoordinateReferenceSystem axisCrs, double value) throws FactoryException {
// ensure no cache modify the values
metadata.clearInstancesCache();
final ReferencingBuilder rb = new ReferencingBuilder(metadata);
final GridDomainAccessor acc = new GridDomainAccessor(metadata);
final RectifiedGrid rectifiedGrid = metadata.getInstanceForType(RectifiedGrid.class);
// search for the coordinate reference system
CoordinateReferenceSystem crs = rb.getCoordinateReferenceSystem(CoordinateReferenceSystem.class);
if (crs == null) {
// no crs defined, we can't add any slice axis value
final Logger logger = Logger.getLogger("org.geotoolkit.metadata.geotiff");
logger.info("Tiff has no base CRS, slice dimension crs will not be added.");
return;
}
final List<SingleCRS> crss = CRS.getSingleComponents(crs);
int axisIndex = -1;
int inc = 0;
for (CoordinateReferenceSystem cs : crss) {
if (cs.equals(axisCrs)) {
axisIndex = inc;
break;
}
inc += cs.getCoordinateSystem().getDimension();
}
if (axisIndex < 0) {
// this axis is not declared, add it
crs = new GeodeticObjectBuilder().addName(crs.getName().getCode() + "/" + axisCrs.getName().getCode()).createCompoundCRS(crs, axisCrs);
rb.setCoordinateReferenceSystem(crs);
// calculate new transform values
final List<double[]> offsetVectors = new ArrayList(rectifiedGrid.getOffsetVectors());
for (int i = 0; i < offsetVectors.size(); i++) {
double[] vector = offsetVectors.get(i);
vector = Arrays.copyOf(vector, vector.length + 1);
offsetVectors.set(i, vector);
}
final double[] tempVector = new double[crs.getCoordinateSystem().getDimension()];
tempVector[tempVector.length - 1] = 1;
offsetVectors.add(tempVector);
// new origin
final DirectPosition oldOrigin = rectifiedGrid.getOrigin();
final GeneralDirectPosition newOrigin = new GeneralDirectPosition(crs);
for (int i = 0, n = oldOrigin.getDimension(); i < n; i++) {
newOrigin.setOrdinate(i, oldOrigin.getOrdinate(i));
}
newOrigin.setOrdinate(oldOrigin.getDimension(), value);
// new limits
final int[][] limits = acc.getLimits();
limits[0] = Arrays.copyOf(limits[0], limits[0].length + 1);
limits[1] = Arrays.copyOf(limits[1], limits[1].length + 1);
// limits[1][limits[1].length-1] = 1;
// set new values
acc.setOrigin(newOrigin.getCoordinate());
acc.setLimits(limits[0], limits[1]);
acc.clearOffsetVectors();
for (double[] ov : offsetVectors) {
acc.addOffsetVector(ov);
}
} else {
// axis already exist, update the value
// new origin
final DirectPosition oldOrigin = rectifiedGrid.getOrigin();
final GeneralDirectPosition newOrigin = new GeneralDirectPosition(oldOrigin);
newOrigin.setOrdinate(axisIndex, value);
// set new values
acc.setOrigin(newOrigin.getCoordinate());
}
// metadata keeps a cache of object likes crs, rectifiedgrid and so on ...
// we must clear them since we modifyed the sub nodes
metadata.clearInstancesCache();
}
use of org.opengis.coverage.grid.RectifiedGrid in project geotoolkit by Geomatys.
the class GeoTiffMetaDataWriter method fillMetadata.
/**
* Complete the TIFF metadata tree with geotiff informations.
*/
public void fillMetadata(Node tiffTree, final SpatialMetadata spatialMD) throws ImageMetadataException, IOException, FactoryException, TransformException {
ArgumentChecks.ensureNonNull("tiffTree", tiffTree);
ArgumentChecks.ensureNonNull("spatialMD", spatialMD);
// container for informations which will be written
final GeoTiffMetaDataStack stack = new GeoTiffMetaDataStack(tiffTree);
// fill geotiff crs information
final CoordinateReferenceSystem coverageCRS = spatialMD.getInstanceForType(CoordinateReferenceSystem.class);
final GeoTiffCRSWriter crsWriter = new GeoTiffCRSWriter();
crsWriter.fillCRSMetaDatas(stack, CRSUtilities.getCRS2D(coverageCRS));
// fill the transformation information
final RectifiedGrid domain = spatialMD.getInstanceForType(RectifiedGrid.class);
AffineTransform gridToCrs = MetadataHelper.INSTANCE.getAffineTransform(domain, null);
// readjust gridToCRS to be the pixel corner
final Georectified georect = spatialMD.getInstanceForType(Georectified.class);
final CellGeometry cell = georect.getCellGeometry();
PixelOrientation orientation = georect.getPointInPixel();
if (orientation == null)
orientation = PixelOrientation.CENTER;
if (CellGeometry.POINT.equals(cell)) {
stack.addShort(GTRasterTypeGeoKey, RasterPixelIsPoint);
if (!orientation.equals(PixelOrientation.CENTER)) {
AffineTransform2D trs = new AffineTransform2D(gridToCrs);
gridToCrs = (AffineTransform) PixelTranslation.translate(trs, orientation, PixelOrientation.CENTER, 0, 1);
}
} else {
// consider all other as Area
stack.addShort(GTRasterTypeGeoKey, RasterPixelIsArea);
if (!orientation.equals(PixelOrientation.UPPER_LEFT)) {
AffineTransform2D trs = new AffineTransform2D(gridToCrs);
gridToCrs = (AffineTransform) PixelTranslation.translate(trs, orientation, PixelOrientation.UPPER_LEFT, 0, 1);
}
}
// -- find a date from crs
final int tempOrdinate = getTemporalOrdinate(coverageCRS);
if (tempOrdinate >= 0) {
// -- add temporal tag
final GridDomainAccessor gda = new GridDomainAccessor(spatialMD);
final double[] origin = gda.getAttributeAsDoubles("origin", false);
final double date = origin[tempOrdinate];
final Date dat = DefaultTemporalCRS.castOrCopy(CommonCRS.Temporal.JAVA.crs()).toDate(date);
stack.setDate(dat);
}
fillTransform(stack, gridToCrs, domain.getExtent());
// fill NoData values
fillSampleDimensionProperties(stack, spatialMD);
// write in the metadata tree
stack.flush();
}
use of org.opengis.coverage.grid.RectifiedGrid in project geotoolkit by Geomatys.
the class MetadataProxy method invoke.
/**
* Invoked when a method from the metadata interface has been invoked.
*
* @param proxy The proxy instance that the method was invoked on.
* @param method The method from the interface which have been invoked.
* @param args The arguments, or {@code null} if the method takes no argument.
* @return The value to return from the method invocation on the proxy instance.
* @throws UnsupportedOperationException If the given method is not supported.
* @throws IllegalArgumentException If {@code args} contains a value while none was expected.
* @throws IllegalStateException If the attribute value can not be converted to the return type.
*/
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws UnsupportedOperationException, IllegalArgumentException, IllegalStateException {
/*
* We accept only calls to getter methods, except for a few non-final
* methods defined in Object that we need to define for compliance.
*/
final String methodName = method.getName();
final int numArgs = (args != null) ? args.length : 0;
if (!methodName.startsWith("get") && !methodName.startsWith("is")) {
switch(numArgs) {
case 0:
{
if (methodName.equals("toString")) {
return toProxyString();
}
if (methodName.equals("hashCode")) {
return System.identityHashCode(proxy);
}
break;
}
case 1:
{
if (methodName.equals("equals")) {
return proxy == args[0];
}
break;
}
}
throw new UnsupportedOperationException(Errors.format(Errors.Keys.UnknownCommand_1, methodName));
}
/*
* If an argument is provided to the method, this is an error since we don't know
* how to handle that, except for a few hard-coded special cases.
*/
if (numArgs != 0) {
if (SPECIAL_CASE && numArgs == 1) {
final Object arg = args[0];
if (arg instanceof Integer) {
final int dim = (Integer) arg;
if (proxy instanceof GridEnvelope) {
switch(methodName) {
case "getLow":
return (long) getAttributeAsInteger("low", dim);
case "getHigh":
return (long) getAttributeAsInteger("high", dim);
case "getSize":
return (long) getAttributeAsInteger("high", dim) - (long) getAttributeAsInteger("low", dim) + 1;
}
}
}
}
throw new IllegalArgumentException(Errors.format(Errors.Keys.UnexpectedArgumentForInstruction_1, methodName));
}
/*
* Gets the name of the attribute to fetch, and set the accessor
* child index on the children represented by this proxy (if any).
*/
final MetadataNodeParser accessor = this.accessor;
final String name = getAttributeName(methodName);
if (index >= 0) {
accessor.selectChild(index);
} else {
accessor.selectParent();
}
/*
* First, process the cases that are handled in a special way. The order is significant:
* if the target type is some generic type like java.lang.Object, then we want to select
* the method performing the less transformation (String if the target type is Object,
* Double rather than Integer if the target type is Number).
*/
final Class<?> targetType = method.getReturnType();
if (targetType.equals(Double.TYPE)) {
Double value = accessor.getAttributeAsDouble(name);
if (value == null)
value = Double.NaN;
return value;
}
if (targetType.equals(Float.TYPE)) {
Float value = accessor.getAttributeAsFloat(name);
if (value == null)
value = Float.NaN;
return value;
}
if (targetType.equals(Long.TYPE)) {
Integer value = accessor.getAttributeAsInteger(name);
return Long.valueOf((value != null) ? value.longValue() : 0L);
}
if (targetType.equals(Integer.TYPE)) {
Integer value = accessor.getAttributeAsInteger(name);
if (value == null) {
value = 0;
if (SPECIAL_CASE) {
if (methodName.equals("getDimension")) {
if (proxy instanceof GridEnvelope) {
value = Math.max(getAttributeLength("low"), getAttributeLength("high"));
} else if (proxy instanceof RectifiedGrid) {
final double[] values = accessor.getAttributeAsDoubles("origin", false);
if (values != null)
value = values.length;
}
}
}
}
return value;
}
if (targetType.equals(Short.TYPE)) {
Integer value = accessor.getAttributeAsInteger(name);
return Short.valueOf((value != null) ? value.shortValue() : (short) 0);
}
if (targetType.equals(Byte.TYPE)) {
Integer value = accessor.getAttributeAsInteger(name);
return Byte.valueOf((value != null) ? value.byteValue() : (byte) 0);
}
if (targetType.equals(Boolean.TYPE)) {
Boolean value = accessor.getAttributeAsBoolean(name);
if (value == null)
value = Boolean.FALSE;
return value;
}
if (targetType.isAssignableFrom(String.class))
return accessor.getAttribute(name);
if (targetType.isAssignableFrom(Double.class))
return accessor.getAttributeAsDouble(name);
if (targetType.isAssignableFrom(Float.class))
return accessor.getAttributeAsFloat(name);
if (targetType.isAssignableFrom(Integer.class))
return accessor.getAttributeAsInteger(name);
if (targetType.isAssignableFrom(Boolean.class))
return accessor.getAttributeAsBoolean(name);
if (targetType.isAssignableFrom(String[].class))
return accessor.getAttributeAsStrings(name, false);
if (targetType.isAssignableFrom(double[].class))
return accessor.getAttributeAsDoubles(name, false);
if (targetType.isAssignableFrom(float[].class))
return accessor.getAttributeAsFloats(name, false);
if (targetType.isAssignableFrom(int[].class))
return accessor.getAttributeAsIntegers(name, false);
if (targetType.isAssignableFrom(Date.class))
return accessor.getAttributeAsDate(name);
if (targetType.isAssignableFrom(NumberRange.class))
return accessor.getAttributeAsRange(name);
if (targetType.isAssignableFrom(Citation.class))
return accessor.getAttributeAsCitation(name);
if (targetType.isAssignableFrom(InternationalString.class)) {
return Types.toInternationalString(accessor.getAttribute(name));
}
if (targetType.isAssignableFrom(Unit.class)) {
final Class<?> bounds = Classes.boundOfParameterizedProperty(method);
return accessor.getAttributeAsUnit(name, asSubclassOrNull(bounds, Quantity.class));
}
if (SPECIAL_CASE && Character.isLowerCase(name.charAt(0))) {
/*
* We have not yet defined a good public API for those cases. For the time being,
* we use the case of the node name in order to detect if we have an attribute
* instead than an element.
*/
if (targetType.isAssignableFrom(GeneralDirectPosition.class)) {
final double[] coordinates = accessor.getAttributeAsDoubles(name, false);
return (coordinates != null) ? new GeneralDirectPosition(coordinates) : null;
}
}
if (targetType.isAssignableFrom(List.class)) {
/*
* The return type is a list or a collection. If the collection elements are some
* simple types, then we assume that it still an attribute. We do not cache this
* value because we want to recompute it on next method call (the returned list
* is not "live").
*/
Class<?> componentType = Classes.boundOfParameterizedProperty(method);
if (componentType.isAssignableFrom(String.class)) {
return UnmodifiableArrayList.wrap(accessor.getAttributeAsStrings(name, false));
}
if (componentType.isAssignableFrom(InternationalString.class)) {
return UnmodifiableArrayList.wrap(Types.toInternationalStrings(accessor.getAttributeAsStrings(name, false)));
}
if (componentType.isAssignableFrom(Citation.class)) {
final Citation c = accessor.getAttributeAsCitation(name);
if (c == null)
return Collections.emptyList();
return Collections.singletonList(accessor.getAttributeAsCitation(name));
}
/*
* Assume a nested element. We will create a "live" list and cache it for future
* reuse. The type of list elements may be a subtype of 'componentType' because
* IIOMetadataFormat may specify restrictions.
*/
if (childs == null) {
childs = new HashMap<>();
}
List<?> list = (List<?>) childs.get(methodName);
if (list == null) {
String elementName = SpatialMetadataFormat.toElementName(name);
final MetadataNodeParser acc;
try {
acc = new MetadataNodeParser(accessor, elementName, "#auto");
} catch (IllegalArgumentException e) {
/*
* This exception happen when no node for 'elementName' is defined in the
* IIOMetadataFormat used by the accessor. For example, DiscoveryMetadata
* node in stream SpatialMetadataFormat omits the 'languages' attribute.
*/
Logging.recoverableException(MetadataNodeParser.LOGGER, interfaceType, methodName, e);
return null;
} catch (NoSuchElementException e) {
// There is no value for this node.
return Collections.emptyList();
}
/*
* At this point we have a MetadataNodeParser to a node which is known to exist.
* This node may have no children, in which case we need to wraps the singleton
* in a list.
*/
if (acc.allowsChildren()) {
componentType = getElementClass(acc.childPath, componentType);
list = acc.newProxyList(componentType);
} else {
componentType = getElementClass(elementName, componentType);
list = Collections.singletonList(acc.newProxyInstance(componentType));
}
childs.put(methodName, list);
}
return list;
}
/*
* Code lists case. Only existing instances are returned; no new instance is created.
*/
if (CodeList.class.isAssignableFrom(targetType)) {
@SuppressWarnings({ "unchecked", "rawtypes" }) final CodeList code = accessor.getAttributeAsCode(name, (Class) targetType);
return code;
}
if (Enum.class.isAssignableFrom(targetType)) {
@SuppressWarnings({ "unchecked", "rawtypes" }) final Enum code = accessor.getAttributeAsEnum(name, (Class) targetType);
return code;
}
/*
* For all other types, assume a nested child element.
* A new proxy will be created for the nested child.
*/
if (childs == null) {
childs = new HashMap<>();
}
Object child = childs.get(methodName);
if (child == null) {
try {
// Each of the last 3 lines may throw, directly or indirectly, an IllegalArgumentException.
final String elementName = SpatialMetadataFormat.toElementName(name);
final Class<?> elementType = getElementClass(elementName, targetType);
final MetadataNodeParser acc = new MetadataNodeParser(accessor, elementName, "#auto");
child = acc.isEmpty() ? Void.TYPE : acc.newProxyInstance(elementType);
} catch (IllegalArgumentException e) {
/*
* Report the warning and remember that we can not return a value for this
* element, so we don't try again next time. We use a lower warning level
* since this exception can be considered normal (IIOMetadataFormat does
* not define every attributes).
*/
accessor.warning(Level.FINE, interfaceType, methodName, e);
child = Void.TYPE;
} catch (NoSuchElementException e) {
// There is no value for this node.
child = Void.TYPE;
}
childs.put(methodName, child);
}
return (child != Void.TYPE) ? child : null;
}
use of org.opengis.coverage.grid.RectifiedGrid in project geotoolkit by Geomatys.
the class MetadataHelperTest method testAffineTransform.
/**
* Tests the {@link MetadataHelper#getAffineTransform} method.
* Contains also opportunist tests of the following methods:
* <p>
* <ul>
* <li>{@link MetadataHelper#getCellDimension}</li>
* <li>{@link MetadataHelper#getCellSize}</li>
* <li>{@link MetadataHelper#formatCellDimension}</li>
* <li>{@link MetadataHelper#getGridToCRS}</li>
* </ul>
*
* @throws ImageMetadataException Should not happen.
*/
@Test
public void testAffineTransform() throws ImageMetadataException {
// Creates a simple metadata.
final SpatialMetadata metadata = new SpatialMetadata(SpatialMetadataFormat.getImageInstance(GEOTK_FORMAT_NAME));
final GridDomainAccessor accessor = new GridDomainAccessor(metadata);
accessor.setOrigin(-10, -20);
accessor.addOffsetVector(3, 4);
accessor.addOffsetVector(0, 8);
// Tests the metadata.
final MetadataHelper hlp = new MetadataHelper(this);
final RectifiedGrid grid = metadata.getInstanceForType(RectifiedGrid.class);
final AffineTransform tr = hlp.getAffineTransform(grid, null);
assertEquals(-10, tr.getTranslateX(), EPS);
assertEquals(-20, tr.getTranslateY(), EPS);
assertEquals(3, tr.getScaleX(), EPS);
assertEquals(8, tr.getScaleY(), EPS);
assertEquals(0, tr.getShearX(), EPS);
assertEquals(4, tr.getShearY(), EPS);
assertEquals("Testing origin", new Point2D.Double(-10, -20), tr.transform(new Point2D.Double(), null));
assertEquals("Testing offset vector 1", new Point2D.Double(3, 4), tr.deltaTransform(new Point2D.Double(1, 0), null));
assertEquals("Testing offset vector 2", new Point2D.Double(0, 8), tr.deltaTransform(new Point2D.Double(0, 1), null));
assertNull(hlp.getCellDimension(tr));
try {
hlp.getCellSize(tr);
fail("Should not allow to compute a cell size.");
} catch (ImageMetadataException e) {
// This is the expected exception.
}
assertEquals("5 × 8", hlp.formatCellDimension(grid, null));
/*
* getGridCRS(...) should returns an equivalent transform.
*/
final MathTransform gridToCRS = hlp.getGridToCRS(grid);
assertTrue(gridToCRS instanceof AffineTransform);
assertTrue(tr.equals(gridToCRS));
}
use of org.opengis.coverage.grid.RectifiedGrid in project geotoolkit by Geomatys.
the class MetadataProxyTest method testRectifiedGrid.
/**
* Tests the {@code RectifiedGridDomain} node.
*
* @since 3.16
*/
@Test
public void testRectifiedGrid() {
final SpatialMetadata metadata = new SpatialMetadata(SpatialMetadataFormat.getImageInstance(GEOTK_FORMAT_NAME));
final MetadataNodeAccessor rootAccessor = new MetadataNodeAccessor(metadata, null, "RectifiedGridDomain", null);
rootAccessor.setAttribute("origin", -180.0, 90.0);
MetadataNodeAccessor accessor = new MetadataNodeAccessor(rootAccessor, "Limits", null);
accessor.setAttribute("low", new int[] { 0, 0 });
accessor.setAttribute("high", new int[] { 719, 359 });
accessor = new MetadataNodeAccessor(rootAccessor, "OffsetVectors", "OffsetVector");
accessor.selectChild(accessor.appendChild());
accessor.setAttribute("values", 0.5, 0);
accessor.selectChild(accessor.appendChild());
accessor.setAttribute("values", 0, -0.5);
/*
* Build the metadata proxy.
*/
final RectifiedGrid grid = rootAccessor.newProxyInstance(RectifiedGrid.class);
assertNull(grid.getCells());
assertEquals(2, grid.getExtent().getDimension());
assertEquals(2, grid.getOffsetVectors().size());
assertTrue(Arrays.equals(new double[] { 0.5, 0 }, grid.getOffsetVectors().get(0)));
assertTrue(Arrays.equals(new double[] { 0.0, -0.5 }, grid.getOffsetVectors().get(1)));
assertTrue(Arrays.equals(new double[] { -180, 90 }, grid.getOrigin().getCoordinate()));
// assertArrayEquals(new int[] {0, 0}, grid.getExtent().getLow ().getCoordinateValues());
// assertArrayEquals(new int[] {719, 359}, grid.getExtent().getHigh().getCoordinateValues());
assertEquals(0, grid.getExtent().getLow(1));
assertEquals(359, grid.getExtent().getHigh(1));
assertEquals(360, grid.getExtent().getSize(1));
}
Aggregations