Current Multi-Size API (as of 02.10.2015)

Problem Description

When allowing for variable size RasterDataNodes within a Product instance, a number of existing API properties and methods are misleading, confusing and as such often wrongly used. They must be identified, revised, and possibly reinterpreted or even removed with the aim to create a clean and consistent data model API alowing for both single-size and multi-size products. Reinterpretation may be a simple property or method renaming, but must always include revision of the API documentation and all usages in code.

See also:

Product API

The Product properties to be carefully analysed are (usages include snap-engine, snap-desktop, s1tbx, s2tbx, s3tbx):

Taking the large number of code usages into account, it becomes obvious that these properties cannot be simply removed when switching to multi-size products. So they will have to be reinterpreted to make their use valid in the majority of cases. Anyway, all the usages in the code base have to be visited and checked for validity in the light of multi-size products.

RasterDataNode API

The following RasterDataNode properties and methods may require reinterpretation in the light of multi-size products:

ImageManager API

The following ImageManager methods must be replaced, moved or otherwise refactored-out in the light of multi-size products:

(1) Returning the identity transformation by default is probably wrong in many cases, because then images of any size would return the same transformation into a common model CRS!

(2) If (1) holds, the returned multi-level models are also wrong!

GeoCoding API

Placemark API

Target Multi-Size API (Proposal)

Product API

The following is a proposal for a reinterpretation of Product properties and methods and a description of new ones: 

  1. A data Product contains
    1. Vector data
    2. Raster data,
      1. with same or different image raster sizes
      2. with same or different or without geo-coding assigned
  2. A product has a model coordinate reference system (model CRS) which is common to all the product components
    1. Vector data geometries are always given in model coordinates(1)
    2. Raster data images provide an AffineTransformation from image pixel coordinates to model coordinates
  3. A product must always return valid model CRS via Product.getModelCrs(). Either the model CRS
    1. is passed to a constructor, or
    2. is explicitly set using Product.setModelCrs(crs), or
    3. is derived from a reference raster data node(2)(3), or if no such exists
    4. an IllegalStateException is thrown
  4. A product must return a non-zero scene raster size via Product.getSceneRasterSize(). Note that Pins and GCPs have a pixelPos attribute that refers to the reference raster's grid. Also, TiePointGrids interpolate from their local raster data grid into the reference raster's grid. Either the scene raster size
    1. is passed to a constructor, or
    2. is explicitly set using Product.setSceneRasterSize(size), or
    3. is derived from a reference raster data node, or if no such exists
    4. an IllegalStateException is thrown
  5.  A product must return an affine transformation from the reference raster grid to the model CRS (i2m-transform) via Product.getSceneImageToModelTransform(). Either the i2m-transform
    1. is passed to a constructor, or
    2. is explicitly set using Product.setSceneImageToModelTransform(i2mT), or
    3. is derived from a reference raster data node(4), or if no such exists
    4. an IllegalStateException is thrown
  6. A product may return a scene geo-coding via Product.getSceneGeoCoding() providing the transformation from the scene raster coordinates to geographical coordinates. Therefore, a reference geo-coding implies a valid scene raster size. Either the scene geo-coding
    1. is passed to a constructor, or
    2. is explicitly set using Product.setSceneGeoCoding(geoCoding), or
    3. is derived from a reference raster data node, or if no such exists
    4. null is returned
  7. A product must return a preferred tile size for all raster data nodes(5) via Product.getPreferredTileSize(). Either the preferred tile size 
    1. is explicitely set by Product.setPreferredTileSize(tw,th), or
    2. a default tile size is computed from the reference raster size, or if no such exists
    3. (512, 512) is returned.

(1) Vector data are represented by SimpleFeature which can provide a CRS for their default geometry. Howver, in SNAP we use a common coordinate system for all vector data therefore a single model CRS is required per product. Pins and GCPs also have a pixelPos attribute. Its value must be given in reference raster coordinates.

(2) Obtaining the reference raster data node could be done as follows:

(3) See implementation and review role of ImageManager.getModelCrs(GeoCoding) method.

(4) See implementation and review role of ImageManager.getImageToModelTransform(GeoCoding) method.

(5) A client may wish to force all images of all raster data nodes to have the same tile size regardless of their actual image size. This may or may not be useful for a particular product type instance. Also, it may make sense to force the tile cache to contain single size tiles for maximum memory reuse.

RasterDataNode API

  1. A raster data node may return its own geoCoding. Either the geo-coding is
    1. the one explicitly set by RasterDataNode.setGeoCoding(gc), or
    2. derived from it's parent product's Product.sceneGeoCoding, but only if is has been explicitly set (1), or
    3. null.
  2. A raster data node must return its own affine i2m-transform (see above) getImageToModelTransform() that is used by it's source image or that will be used for any source image as long as it hasn't been created so far. Either the i2m-transform is
    1. retrieved from a source image, or if no such yet exists,
    2. the one explicitly set by RasterDataNode.setImageToModelTransform(i2m), or
    3. derived from its own geoCoding and the parent product's Product.modelCrs, or if this is not possible (2) 
    4. an IllegalStateException is thrown
  3. A raster data node must return a tileSize that is used by it's source image or that will be used for any source image as long as it hasn't been created so far. Either the tile size is
    1. retrieved from a source image, or if no such yet exists,
    2. the one explicitly set by RasterDataNode.setTileSize(tw,th), or
    3. the value of Product.preferredTileSize

(1) Avoid infinite recursion, see reference raster data node

(2) The transformation from RasterDataNode.geoCoding.imageCrs to the parent product's Product.modelCrs must be defined and must be affine.

Facts and Consequences

Questions and Answers

  1. Concerning points 1 to 6 and considering the actual use of these properties: is default a better name than reference, or even stay with scene?
    Answer (NF): scene is not bad because it refers to the satellite footprint or spatial coverage, reference iis more neutral (because we don't always have footprints or spatial coverage), default is only useful concerning the Product.addBand(name,...) and addMask(name, ..) methods. May may stay with scene, to minimze refactorings and maximize compatibility. Problematic are other uses of scene such as in ProductSceneView. On the other hand, the Layers used in such a view all share the model CRS. We could argument, that whenever we say scene, we refer to model coordinates.
    Decision: We stay with scene in API names which may be derived from a reference raster data node.
  2. RasterDataNode currently has a rasterSize property and also a sceneRasterSize property which are the same in most cases, but usually different for a TiePointGrid. Once sceneRasterSize is reinterpreted in this new sense, then rasterSize should refer to the actual size of the raster data in use. But since a TiePointGrid automatically expand its data to the sceneRasterSize, its actual raster size is the raster size. How can we avoid this ambiguity?
    Answer (NF): Hide the actual tie point grid size as an implementation detail in TiePointGrid 
    1. Carefully visit and analyse all current RasterDataNode.rasterWith/Height/Size as well as RasterDataNode.get/setRasterData and RasterDataNode.getSceneRasterData usages.
    2. Then let RasterDataNode.rasterWith/Height/Size return the actual raster size with respect to a multi-size product, change context code accordingly (should only affect TiePointGrid usages).
    3. Replace current RasterDataNode.sceneRasterWidth/Height/Size usages by RasterDataNode.rasterWith/Height/Size.
    4. Completely remove RasterDataNode.sceneRasterWidth/Height/Size properties.  
    5. Replace current RasterDataNode.getSceneRasterData usages by RasterDataNode.getRasterData.
    6. Completely remove RasterDataNode.sceneRasterData method.   
  3. Why do we need a pixelPos attribute for Pins and GCPs? The product's model CRS is either the GeoCoding.mapCrs or some image CRS referring to the reference raster size. Therefore there is always a linear transformation from the model CRS to the image CRS anyway.
    Answer (NF): Check actual usage of pixelPos. If it is not too much work, remove the attribute completly, use the geometry field instead as it seems the most generic and non-redundant information.

Implementation