D カスタム・イメージ・レンダラの作成および登録

この付録では、イメージ・テーマで使用するためのカスタム・イメージ・レンダラの実装および登録方法について説明します。

MapViewerでサポートされていない画像形式のイメージ・テーマを指定したマップ・リクエストを作成する場合、最初にその形式に対応したカスタム・イメージ・レンダラを実装し、登録する必要があります。たとえば、3.1.1.6項の例3-6のECW形式はMapViewerでサポートされていません。したがって、この例を機能させるには、まずECW形式の画像に対応したイメージ・レンダラを実装し、登録する必要があります。

インタフェースoracle.sdovis.CustomImageRendererは、Oracle Fusion Middleware環境の$ORACLE_HOME/lbs/libディレクトリに格納された、パッケージsdovis.jarで定義されています。このインタフェースのソース・コードを次に示します。

/**
* An interface for a custom image painter that supports user-defined image
* formats. An implementation of this interface can be registered with
* MapViewer to support a custom image format.
*/
public interface CustomImageRenderer
{
/**
* The method is called by MapViewer to find out the image format
* supported by this renderer. <br>
* This format string must match the one specified in a custom image renderer
* element defined in the configuration file (mapViewerConfig.xml).
*/
public String getSupportedFormat() ;
/**
* Renders the given images. MapViewer calls this method
* to tell the implementor the images to render, the current map
* window in user space, and the MBR (in the same user space) for each
* image.
* <br>
* The implementation should not retain any reference to the parameters
* permanently.
* @param g2 the graphics context to draw the images onto.
* @param images an array of image data stored in byte array.
* @param mbrs an array of double[4] arrays containing one MBR for each
* image in the images array.
* @param dataWindow the data space window covered by the current map.
* @param deviceView the device size and offset.
* @param at the AffineTransform using which you can transform a point
* in the user data space to the device coordinate space. You can
* ignore this parameter if you opt to do the transformation
* yourself based on the dataWindow and deviceView information.
* @param scaleImage a flag passed from MapViewer to indicate whether
* the images should be scaled to fit the current device window.
* If it is set to false, render the image as-is without
* scaling it.
*/
public void renderImages(Graphics2D g2, byte[][] images, double[][] mbrs,
Rectangle2D dataWindow, Rectangle2D deviceView,
AffineTransform at, boolean scaleImage) ;
}

このインタフェースを実装した後には、MapViewerのCLASSPATH定義に含まれるディレクトリ($MAPVIEWER/web/WEB-INF/libディレクトリなど)に、実装クラスを格納する必要があります。実際のレンダリングの実行にネイティブ・ライブラリを使用する場合、そうしたライブラリに必要な他のファイル(.dllファイル、.soファイルなど)が、MapViewerが動作するJava仮想マシン(JVM)から確実にアクセスできるようにしておく必要があります。

カスタム実装クラスおよび必要なライブラリをMapViewerのCLASSPATHに格納した後、構成ファイルmapViewerConfig.xmlでクラスをMapViewerに登録する必要があります(「マップ・ビジュアライゼーション・コンポーネントの構成」を参照)。このファイルの次の部分を適宜検査および編集します。このファイルは、まだサポートしていない特定の画像形式が出現した場合にロードするクラスを、MapViewerに通知します。

<!-- ****************************************************************** -->
<!-- ******************** Custom Image Renderers ********************** -->
<!-- ****************************************************************** -->
<!-- Uncomment and add as many custom image renderers as needed here,
each in its own <custom_image_renderer> element. The "image_format"
attribute specifies the format of images that are to be custom
rendered using the class with the full name specified in "impl_class".
You are responsible for placing the implementation classes in the
MapViewer classpath.
-->
<!--
<custom_image_renderer image_format="ECW"
impl_class="com.my_corp.image.ECWRenderer"/>
-->

この例では、イメージ・テーマの<jdbc_image_query>要素によってロードされるECW形式の画像データについてレンダリングを実行するために、MapViewerによってクラスcom.my_corp.image.ECWRendererがロードされます。

例C-1は、oracle.sdovis.CustomImageRendererインタフェースの実装例です。この例では、ECW画像形式に対応したカスタム・レンダラを実装します。この例は、実例を示すことのみを目的としており、表示したコードは、システム環境によっては最適でなかったり間違ったりしている可能性があるので注意してください。この実装ではECW Java SDKが使用され、ECW Java SDKでは付属のネイティブCライブラリが使用されます。MapViewerでネイティブな動的ライブラリを検索できるようにするには、MapViewerが含まれるインスタンスを起動する際に、コマンドライン・オプション-Djava.library.pathの使用が必要になる場合があります。

例D-1 ECW画像形式に対応したカスタム・イメージ・レンダラ

package com.my_corp.image;
import java.io.*;
import java.util.Random;
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import oracle.sdovis.CustomImageRenderer;
import com.ermapper.ecw.JNCSFile; // from ECW Java SDK
public class ECWRenderer implements CustomImageRenderer {
    String tempDir = null;
    Random random = null;
    public ECWRenderer() {
        tempDir = System.getProperty("java.io.tmpdir");
        random = new Random(System.currentTimeMillis());
    }
    public String getSupportedFormat() {
        return "ECW";
    }
    public void renderImages(Graphics2D g2, byte[][] images,
        double[][] mbrs,
        Rectangle2D dataWindow,
        Rectangle2D deviceView,
        AffineTransform at) {
        // Taking the easy way here; you should try to stitch the images
        // together here.
        for (int i = 0; i < images.length; i++) {
            String tempFile = writeECWToFile(images[i]);
            paintECWFile(tempFile, g2, mbrs[i], dataWindow, deviceView, at);
        }
    }
    private String writeECWToFile(byte[] image) {
        long l = Math.abs(random.nextLong());
        String file = tempDir + "ecw" + l + ".ecw";
        try {
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(image);
            fos.close();
            return file;
        } catch (Exception e) {
            System.err.println("cannot write ecw bytes to temp file: " + file);
            return null;
        }
    }
    private void paintECWFile(String fileName, Graphics2D g,
        double[] mbr,
        Rectangle2D dataWindow,
        Rectangle2D deviceView,
        AffineTransform at) {
        JNCSFile ecwFile = null;
        boolean bErrorOnOpen = false;
        BufferedImage ecwImage = null;
        String errorMessage = null;
        try {
            double dFileAspect, dWindowAspect;
            double dWorldTLX, dWorldTLY, dWorldBRX, dWorldBRY;
            int bandlist[];
            int width = (int) deviceView.getWidth(),
                height = (int) deviceView.getHeight();
            int line, pRGBArray[] = null;
            ecwFile = new JNCSFile(fileName, false);
            // Work out the correct aspect for the setView call.
            dFileAspect = (double) ecwFile.width / (double) ecwFile.height;
            dWindowAspect = deviceView.getWidth() / deviceView.getHeight();
            if (dFileAspect > dWindowAspect) {
                height = (int)((double) width / dFileAspect);
            } else {
                width = (int)((double) height * dFileAspect);
            }
            // Create an image of the ecw file.
            ecwImage = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);
            pRGBArray = new int[width];
            // Set up the view parameters for the ecw file.
            bandlist = new int[ecwFile.numBands];
            for (int i = 0; i < ecwFile.numBands; i++) {
                bandlist[i] = i;
            }
            dWorldTLX = ecwFile.originX;
            dWorldTLY = ecwFile.originY;
            dWorldBRX = ecwFile.originX +
                (double)(ecwFile.width - 1) * ecwFile.cellIncrementX;
            dWorldBRY = ecwFile.originY +
                (double)(ecwFile.height - 1) * ecwFile.cellIncrementY;
            dWorldTLX = Math.max(dWorldTLX, dataWindow.getMinX());
            dWorldTLY = Math.max(dWorldTLY, dataWindow.getMinY());
            dWorldBRX = Math.min(dWorldBRX, dataWindow.getMaxX());
            dWorldBRY = Math.min(dWorldBRY, dataWindow.getMaxY());
            // Set the view.
            ecwFile.setView(ecwFile.numBands, bandlist, dWorldTLX,
                dWorldTLY, dWorldBRX, dWorldBRY, width, height);
            // Read the scan lines.
            for (line = 0; line < height; line++) {
                ecwFile.readLineRGBA(pRGBArray);
                ecwImage.setRGB(0, line, width, 1, pRGBArray, 0, width);
            }
        } catch (Exception e) {
            e.printStackTrace(System.err);
            bErrorOnOpen = true;
            errorMessage = e.getMessage();
            g.drawString(errorMessage, 0, 50);
        }
        // Draw the image (unscaled) to the graphics context.
        if (!bErrorOnOpen) {
            g.drawImage(ecwImage, 0, 0, null);
        }
    }
}