.. _jaiext: ***************** JAI-EXT Tutorial ***************** Introduction ============= From GeoTools 14.x a new JAI extension API has been integrated, this new API is called `JAI-EXT `_. ``JAI-EXT`` is an open-source Project which provides a fast, high-scalability API for image processing. The main feature of this API is the ability to support external ``ROI`` objects and Image ``NoData`` for most of its processing operations. This page has been written to describe this new API inside GeoTools, to demonstrate its use, and to explain best practices for working with it. Usage ======= JAI-EXT registration --------------------- A project which would like to use JAI-EXT operations needs to first register them. This may be accomplished by calling, at the top of your project, the following piece of code: .. code-block:: java static { JAIExt.initJAIEXT(); } This registers all of the JAI-EXT operations inside the JAI ``OperationRegistry`` in order to use them instead of old JAI operations. .. note:: It should be pointed out that if this method is called more than one time; it has no effect since it is only an initialization method. The above changes can be reverted individually:: JAIExt.registerJAIDescriptor("Warp") --> Replace the JAI-EXT "Warp" operation with the JAI one JAIExt.registerJAIEXTDescriptor("Warp") --> Replace the JAI "Warp" operation with the JAI-EXT one These methods allow replacement of the ``OperationDescriptor`` associated with an operation with one from JAI or JAI-EXT. .. note:: ``OperationDescriptor`` is a class describing the parameters to set for executing a JAI/JAI-EXT operation. In order to avoid exceptions after replacing the ``OperationDescriptor`` associated with a JAI/JAI-EXT operation, users should take care on how they launch the operation: #. Using a ``ParameterBlock`` instance, which does not provide the same checks present in the ``ParameterBlockJAI`` class which may lead to unexpected exceptions. Here is an example .. code-block:: java // Inputs RenderedImage img; // Example image (256x256) double[] scales = new double[]{2.0d}; double[] offsets = new double[]{1.0d}; // Input NoData, for example 128. It must be passed as a JAI-EXT "Range" Range nodata = RangeFactory.create(128d, 128d); // Input ROI ROI roi = new ROIShape(new Rectangle(0, 0, 128, 128)); // Optional input RenderingHints RenderingHints hints; // Creation of the ParameterBlock ParameterBlock pb = new ParameterBlock(); // Setting the parameters pb.setSource(img, 0); // The source image. pb.set(scales, 0); // The per-band constants to multiply by. pb.set(offsets, 1); // The per-band offsets to be added. pb.set(roi, 2); // ROI pb.set(nodata, 3); // NoData range // Calling the operation RenderedOp result = JAI.create("Rescale", pb, hints); #. Call the related GeoTools ``ImageWorker`` method, if present: .. code-block:: java // Same inputs as above // Instantiation of the ImageWorker ImageWorker w = new ImageWorker(img); // Setting RenderingHints w.setRenderingHints(img); // Setting ROI and NoData w.setROI(roi); w.setNoData(nodata); // Executing the operation w.rescale(scales, offsets); // Getting the result RenderedOp result = w.getRenderedImage(); .. note:: The main aim of the JAI-EXT project is to completely replace all the JAI framework. In this temporary phase users may notice a few error messages on startup similar to this one: .. code-block:: bash Error in registry file at line number #5 A descriptor is already registered against the name "OrderedDither" under registry mode "rendered" These errors are reported by JAI at low level and will be removed when JAI has been totally replaced. GeoTools registration ---------------------- Since the majority of the GeoTools operations are internally bound to the JAI operations, users must take care on how they handle them with JAI-EXT. The first suggestion is to always use a ``CoverageProcessor`` instance for getting a GeoTools coverage *operation*. It should be better to get a new ``CoverageProcessor`` instance by using the static factory method ``CoverageProcessor.getInstance()`` since this method allows to cache the various ``CoverageProcessor`` instances and reuse them if needed. When an ``OperationDescriptor`` is replaced, users should take care to remove the existing associated GeoTools operation from all the ``CoverageProcessor`` instances and then to insert it again. This procedure must be done in order to avoid having a GeoTools operation with an internal ``OperationDescriptor`` which has been replaced; this situation may lead to wrong parameter initialization which could then lead to exceptions during Coverage processing. The procedure is described below. .. code-block:: java CoverageProcessor.removeOperationFromProcessors("Warp"); // Removal of the operation from the processors CoverageProcessor.updateProcessors(); // Update of all the processors with the new operation Best Practice -------------- Below is a simple piece of code for how to handle ``NoData`` for a ``GridCoverage``. .. code-block:: java // Creation of a new GridCoverage2D from a RenderedImage RenderedImage img; // Example image (256x256) // Coverage CRS CoordinateReferenceSystem crs = CRS.decode("EPSG:4326"); // Coverage Envelope Envelope envelope = new ReferencedEnvelope(-180, 180, -90, 90, crs); // Coverage Properties Map properties = new HashMap(); // NoData definition double nodata = -9999; // Wrapping NoData inside a container NoDataContainer container = new NoDataContainer(nodata); // Setting NoData as property CoverageUtilities.setNoDataProperty(properties, container); // Setting ROI as property ROI roi = new ROIShape(new Rectangle(0, 0, 128, 128)); CoverageUtilities.setROIProperty(properties, roi); // Creating the GridCoverage GridCoverageFactory factory = new GridCoverageFactory(); GridCoverage2D coverage = factory.create("Test", img, envelope); // Retrieving NoData from the GridCoverage NoDataContainer newContainer = CoverageUtilities.getNoDataProperty(coverage); // Retrieving ROI from GridCoverage ROI newROI = CoverageUtilities.getROIProperty(coverage); It should be noted that ``NoData`` is always returned as *NoDataContainer* instance. This class provides useful methods for accessing ``NoData`` as array, single value or ``Range``. In the following code shows how to change the ``NoData`` value after executing a single operation. .. code-block:: java // Getting CoverageProcessor CoverageProcessor processor = CoverageProcessor.getInstance(); // Getting Scale operation Operation scale = processor.getOperation("Scale"); // Getting scale parameters ParameterValueGroup params = processor.getParameters(); params.parameter("Source0").setValue(coverage); // Setting the Background. The first value will be taken as NoData if a NoData was already present in input params.parameter("backgroundValues").setValue(new double[]{100}); // Executing the operation GridCoverage2D result = (GridCoverage2D) processor.doOperation(params); // Getting the new NoData value NoDataContainer newNoDataContainer = CoverageUtilities.getNoDataProperty(result); // it should have 100 as NoData .. warning:: Since the ``GTCrop`` operation has been moved to the JAI-EXT project, users should take care that replacing the JAI-EXT Crop with the JAI one will result in the loss of all the fixes provided by ``GTCrop``.