Multi-Size Product API
Current Multi-Size API (as of 02.10.2015)
Problem Description
When allowing for variable size RasterDataNode
s 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:
- SNAP-255Getting issue details... STATUS
Product API
The Product properties to be carefully analysed are (usages include snap-engine, snap-desktop, s1tbx, s2tbx, s3tbx):
sceneRasterSize
: Common size for allRasterDataNode
s. Is always present. Defines the model coordinate system for data in satellite projection which is used by all vector data. Defines the image coordinate system for a pin's and GCP'spixelPos
attribute. 28 getter usages (no setter available).sceneRaster
Width
andsceneRaster
Height
: SeesceneRasterSize
. 846 getter usages (no setter available).geoCoding
: Common geo-coding for allRasterDataNode
s. Corresponds to the scene raster size and therefore implies it. Provides the transformation from scene raster pixel positions to geographical positions. An overall scene raster size and geo-coding is needed for showing the footprint representation on the world map. 426 getter usages, 222 setter usages.preferredTileSize
: A tile size that may be used when any multi-level images are created for an associatedRasterDataNode
(TiePointGrid
,Band
,VirtualBand
,Mask
). The preferred tile size relates more or less to the scene raster size (more or less, because it is currently used for the lower resolution levels of image pyramids). 46 getter usages, 41 setter usages.numResolutionsMax
: The maximum number of resolution levels for multi-level images of all raster data nodes.
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:
rasterSize
: Refers to the actual size of the stored data hold by a raster data node. It usually equals thesceneRasterSize
with the exception ofTiePointGrid
s wheresceneRasterSize
. 43 getter usages (no setter available)rasterWidth/Height
: SeerasterSize
. 218 getter usages (no setter available)get/setRasterData
: Gets the actually stored raster data corresponing torasterSize
. 58 getter usages, 51 setter usages.sceneRasterSize
: Refers to size of the raster's multi-level source image which may either exist or not-yet-exist. Usually the same as rasterSize with the exception ofTiePointGrid
s where it is the same asProduct.sceneRasterSize
. 4 getter usages (no setter available)sceneRasterWidth/Height
: SeesceneRasterSize
. 187 getter usages (no setter available)getSceneRasterData
: Gets the raster data for the scene corresponing tosceneRsterSize
. 2(?) usages.geoCoding
: The raster data node's geo-coding. If it is not explicitely set, it is the same asProduct
.geoCoding
. 105 getter usages, 48 setter usages.
ImageManager API
The following ImageManager
methods must be replaced, moved or otherwise refactored-out in the light of multi-size products:
getImageToModelTransform(GeoCoding)
: CallsGeoCoding.getImageToMapTransform
to check if it is affine, if so, returns it, otherwise returns identity (1). 22 usagesgetModelCrs(GeoCoding)
: CallsGeoCoding.getImageToMapTransform
to check if it is affine, if so, returnsGeoCoding.mapCrs
, otherwiseGeoCoding.imageCrs
. 22 usagesgetMultiLevelModel(RasterDataNode)
: Used to derive a multi-level image model from a raster data node's source image, otherwise callscreateMultiLevelModel
.(2) 26 usagescreateMultiLevelModel(ProductNode)
: Used to create a default multi-level image model. CallsgetImageToModelTransform
, usesProduct.numResolutionsMax
.(2) 9 usagesgetPreferredTileSize(Product)
Gets the preferred tile size of a product or computes a new one. 16 usages
(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
getImageToMapTransform
()
: Finds a transformation between theGeoCoding.imageCrs
andmapCrs
. 15 usages.
Placemark API
pixelPos
: An attribute of the accociatedSimpleFeatureType
. Stores the scene raster pixel coordinate. Used to store original pixel positions at which users placed a pin or GCP. For all other placemarks than GCPs thegeoPos
attribute is updated frompixelPos
changes.
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:
- A data Product contains
- Vector data
- Raster data,
- with same or different image raster sizes
- with same or different or without geo-coding assigned
- A product has a model coordinate reference system (model CRS) which is common to all the product components
- Vector data geometries are always given in model coordinates(1)
- Raster data images provide an
AffineTransformation
from image pixel coordinates to model coordinates
- A product must always return valid model CRS via
Product.getModelCrs()
. Either the model CRS- is passed to a constructor, or
- is explicitly set using
Product.setModelCrs(crs)
, or - is derived from a reference raster data node(2)(3), or if no such exists
- an
IllegalStateException
is thrown
- A product must return a non-zero scene raster size via
Product.getSceneRasterSize()
. Note thatPin
s andGCP
s have apixelPos
attribute that refers to the reference raster's grid. Also,TiePointGrid
s interpolate from their local raster data grid into the reference raster's grid. Either the scene raster size- is passed to a constructor, or
- is explicitly set using
Product.setSceneRasterSize(size)
, or - is derived from a reference raster data node, or if no such exists
- an
IllegalStateException
is thrown
- 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- is passed to a constructor, or
- is explicitly set using
Product.set
, orScene
Image
ToModelTransform(i2mT) - is derived from a reference raster data node(4), or if no such exists
- an
IllegalStateException
is thrown
- 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- is passed to a constructor, or
- is explicitly set using
Product.setSceneGeoCoding(geoCoding)
, or - is derived from a reference raster data node, or if no such exists
null
is returned
- A product must return a preferred tile size for all raster data nodes(5) via
Product.getPreferredTileSize()
. Either the preferred tile size- is explicitely set by
Product.setPreferredTileSize(tw,th)
, or - a default tile size is computed from the reference raster size, or if no such exists
(512, 512)
is returned.
- is explicitely set by
(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. Pin
s and GCP
s 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:
- From all raster data nodes with geo-coding return the one that has a maximum raster size.
- Otherwise from all raster data nodes return the one that has a maximum raster size.
- Otherwise return
null
.
(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
- A raster data node may return its own
geoCoding
. Either the geo-coding is- the one explicitly set by
RasterDataNode.setGeoCoding(gc)
, or - derived from it's parent product's
Product.sceneGeoCoding
, but only if is has been explicitly set (1), or null.
- the one explicitly set by
- 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- retrieved from a source image, or if no such yet exists,
- the one explicitly set by
RasterDataNode.setImageToModelTransform(i2m)
, or - derived from its own
geoCoding
and the parent product'sProduct.modelCrs
, or if this is not possible (2) - an
IllegalStateException
is thrown
- 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- retrieved from a source image, or if no such yet exists,
- the one explicitly set by
RasterDataNode.setTileSize(tw,th)
, or - 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
- Every product instance must have a model CRS. As a consequence, in order to save products in DIMAP- or -NetCDF format, they must be made persistable.
- A scene geo-coding implies a valid scene raster size.
- A model CRS may be derived from the scene geo-coding, see implementation of
ImageManager.getModelCrs(gc)
- A model CRS may be derived from a valid scene image only, if no scene geo-coding is specified.
- All i2m-transforms are affine (linear) transformations.
- The
Product.
addBand(name, ...)
andaddMask(name, ...)
methods must throw anIllegalStateException
if no valid reference raster size is defined. - Note that it is still confusing to API users that we
- use the terms image, raster, pixel, sample in an inconsistent manner in property/method names in
RasterDataNode
and alsoTile
(GPF); - have map, geo and image CRS in the GeoCoding API, but a model CRS in the Product API (four CRS!). Luckily, API users usually don't deal with that too much.
- use the terms image, raster, pixel, sample in an inconsistent manner in property/method names in
Questions and Answers
- 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 theProduct.addBand(name,...)
andaddMask(name, ..)
methods. May may stay with scene, to minimze refactorings and maximize compatibility. Problematic are other uses of scene such as inProductSceneView
. On the other hand, theLayer
s 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. RasterDataNode
currently has arasterSize
property and also asceneRasterSize
property which are the same in most cases, but usually different for aTiePointGrid
. OncesceneRasterSize
is reinterpreted in this new sense, thenrasterSize
should refer to the actual size of the raster data in use. But since aTiePointGrid
automatically expand its data to thesceneRasterSize
, 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 inTiePointGrid
.- Carefully visit and analyse all current
RasterDataNode.rasterWith/Height/Size
as well asRasterDataNode.
get/setRasterData
andRasterDataNode.
getSceneRasterData
usages. - Then let
RasterDataNode.rasterWith/Height/Size
return the actual raster size with respect to a multi-size product, change context code accordingly (should only affectTiePointGrid
usages). - Replace current
RasterDataNode.sceneRasterWidth/Height/Size
usages byRasterDataNode.rasterWith/Height/Size
. - Completely remove
RasterDataNode.sceneRasterWidth/Height/Size
properties. - Replace current
RasterDataNode.getSceneRasterData
usages byRasterDataNode.
.getRasterData
- Completely remove
RasterDataNode.sceneRasterData
method.
- Carefully visit and analyse all current
- Why do we need a
pixelPos
attribute for Pins and GCPs? The product's model CRS is either theGeoCoding.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 ofpixelPos
. If it is not too much work, remove the attribute completly, use thegeometry
field instead as it seems the most generic and non-redundant information.
Implementation
- revisit existing Product constructors and change according to requirements 1 to 6.
- add
Product.get/setModelCrs
methods. ImplementgetModelCrs
method according to point 3 above - reimplement
Product.getSceneRasterSize
according to point 4 above
- reimplement
Product.
getSceneRasterWidth/Height
- rename
Product.getGeoCoding
intogetSceneGeoCoding
. Implement method according to point 6 above - replace all
ImageManager.getModelCrs(gc)
by Product.getModelCrs() calls, removeImageManager.getModelCrs
- replace all
ImageManager.getImageToModelTransform(gc)
by ??? calls, removeImageManager.getImageToModelTransform