Re: [OpenMap Users] I am attempting to create a geotiff layer

From: Carsten Ø. Madsen <com_at_navicon.dk>
Date: Wed, 05 Oct 2005 10:44:08 +0200

Hi Jason

Here is some code we have done to do JAI supported rasters incl
geotiff's etc. It is a modification of some code by William Art
<artw_at_saharamaps.com> posted on the openmap mailing list previously.

Hope this helps.

regards
/carsten

/*
 * Copyright NAVICON A/S 2005
 * com_at_navicon.dk
 *
 **/

package dk.navicon.openmap.layer;

import java.awt.Component;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;

import javax.media.jai.JAI;
import javax.media.jai.PlanarImage;

import com.bbn.openmap.LatLonPoint;
import com.bbn.openmap.omGraphics.OMGraphicList;
import com.bbn.openmap.omGraphics.OMScalingRaster;
import com.bbn.openmap.plugin.OMGraphicHandlerPlugIn;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.proj.coords.DMSLatLonPoint;
import com.bbn.openmap.util.Debug;
import com.bbn.openmap.util.PropUtils;
import com.sun.media.jai.codec.FileSeekableStream;
import com.sun.media.jai.codec.TIFFDirectory;
import com.sun.media.jai.codec.TIFFField;

/**
 * _at_author com_at_navicon.dk
 *
 * This is a modification of some code by William Art <artw_at_saharamaps.com>
 * posted on the openmap mailing list
 *
 * This class will open any of the following image file formats:
 *
 * <ul>
 * <li>geo tiff
 * <li>jpeg
 * <li>bmp</li>
 * </ul>
 *
 * Only lat/lon projection is supported.
 *
 * Using the java JAI package
(http://java.sun.com/products/java-media/jai/). To
 * use this class you must have the JAI package installed and available
in your
 * classpath when running OpenMap.
 *
 * Properties example:
 *
 * <pre>
 *
 *
 * JAILayer.prettyName=MyJAILayer
 * JAILayer.class=dk.navicon.openmap.layer.JAIPlugIn
 *
JAILayer.imageFile=/opt/Mapdata/AHOWEB_DP001/AUS00605/A00605.tif
 *
 *
 * </pre>
 *
 * Currently the image must be in lat/lon this includes the world file.
If you
 * have geo tiff files in a different format like UTM you can use GDAL to
 * reformat to lat/lon gdalwarp -co INTERLEAVE=PIXEL -t_srs WGS84
 * S_10000_127.tif aarhus-havn.tif
 *
 * World files are assumed to end with w, i.e., xxx.jpg -> xxx.jgw etc.
 *
 */

public class JAIPlugIn extends OMGraphicHandlerPlugIn {

    double lat, lon, lat2, lon2, ximage_scale, yimage_scale;

    // sourcelat,sourcelon;

    protected PlanarImage source = null;

    boolean DEBUG = false;

    /** The property for the data file - tileFile. */
    public final static String FileNameProperty = "imageFile";

    /**
     * The property for whether the data file has a descriptive header
on the
     * first line, to let the reader know to ignore that line -
fileHasHeader.
     * Default is true.
     */

    protected String imageFileName = null;

    // the map
    OMScalingRaster raster = null;

    /**
     * Default constructor.
     */
    public JAIPlugIn() {
        super();
        DEBUG = Debug.debugging("JAIPlugIn");
    }

    public JAIPlugIn(Component comp) {
        super(comp);
    }

    /**
     * The getRectangle call is the main call into the PlugIn module.
The module
     * is expected to fill the graphics list with objects that are
within the
     * screen parameters passed.
     *
     * _at_param p
     * projection of the screen, holding scale, center coords,
     * height, width.
     */
    public OMGraphicList getRectangle(Projection p) {

        OMGraphicList list = (OMGraphicList) getList();
        list.clear();

        if (DEBUG) {
            Debug.output("CSVTIPI: getRectangle");
        }

        if (raster == null) {
            loadRaster(imageFileName);
        }

        if (raster.isOnMap(p)) {
            if (DEBUG) {
                Debug.output("CSVTIPI: image on map");
            }
            raster.generate(p);
            list.add(raster);
        } else if (DEBUG) {
            Debug.output("CSVTIPI: image not on map, skipping");
        }

        repaint();
        return list;

    } // end getRectangle

    /**
     * PropertyConsumer method, setting the PlugIn with properties that
apply to
     * it.
     */
    public void setProperties(String prefix, Properties props) {
        super.setProperties(prefix, props);

        String realPrefix = PropUtils.getScopedPropertyPrefix(prefix);

        imageFileName = props.getProperty(realPrefix + FileNameProperty);

        if (DEBUG) {
            Debug.output("CSVTIPI: file: " + imageFileName);
        }

    }

    /**
     * Takes the URL to a csv file and parses it into OMScaledRasters,
adding
     * them to the tiles HashSet.
     */
    protected void loadRaster(String csvFileName) {
        if (imageFileName != null) {
            try {
                init();
            } catch (FileNotFoundException e) {
                Debug.output("CSVTIPI: file: " + imageFileName + " not
found");
                e.printStackTrace();
            }
            // OMScalingRaster omsr
            raster = new OMScalingRaster((float) lat, (float) lon,
                    (float) lat2, (float) lon2,
source.getAsBufferedImage());
        }
    }

    /**
     * Method to fill in a Properties object, reflecting the current
values of
     * the PropertyConsumer. If the PropertyConsumer has a prefix set, the
     * property keys should have that prefix plus a separating '.'
prepended to
     * each propery key it uses for configuration.
     *
     * _at_param getList
     * a Properties object to load the PropertyConsumer
properties
     * into. If getList equals null, then a new Properties object
     * should be created.
     * _at_return Properties object containing PropertyConsumer property
values. If
     * getList was not null, this should equal getList.
Otherwise, it
     * should be the Properties object created by the
PropertyConsumer.
     */
    public Properties getProperties(Properties getList) {
        if (getList == null) {
            getList = new Properties();
        }

        String prefix = PropUtils.getScopedPropertyPrefix(this);

        getList.put(prefix + FileNameProperty,
PropUtils.unnull(imageFileName));

        return getList;
    }

    int sourceWidth;

    int sourceHeigth;

    public void init() throws FileNotFoundException {
        try {

            String associatedfile = imageFileName;
            source = JAI.create("fileload", associatedfile);

            sourceWidth = source.getWidth();
            sourceHeigth = source.getHeight();

            double xscale, yscale;
            double lonNW, latNW;
            String projfilename = imageFileName;
            FileSeekableStream st;

            if (imageFileName.endsWith(".tif")
                    || imageFileName.endsWith(".TIF")) {

                st = new FileSeekableStream(imageFileName);

                TIFFDirectory td;
                td = new TIFFDirectory(st, 0);

                TIFFField tfMPS = td.getField(33550); // ModelPixelScaleTag
                TIFFField tfMTP = td.getField(33922); // ModelTiepointTag

                System.out.println("tfMPS " + tfMPS + "\ntfMTP " + tfMTP);

                try {
                    xscale = tfMPS.getAsDouble(0);
                    yscale = tfMPS.getAsDouble(1);

                    double[] tiepoints = tfMTP.getAsDoubles();

                    System.out.println("count=" + tfMTP.getCount());

                    for (int i = 0; i < tiepoints.length; i++) {
                        System.out.println("-->" + tiepoints[i]);
                    }

                    System.out.println("type=" + tfMPS.getType());
                    System.out.println("type=" + tfMTP.getType());
                    System.out.println("nb of tiepoints = "
                            + tfMTP.getAsDouble(0));

                    if (tfMTP.getCount() != 6 && tfMTP.getCount() != 4) {
                        System.out
                                .println("warning tif file may pose
problems...");
                    }

                    if (tfMTP.getCount() == 6) {
                        lonNW = tfMTP.getAsDouble(3);
                        latNW = tfMTP.getAsDouble(4);
                    } else if (tfMTP.getCount() == 2) {
                        lonNW = tfMTP.getAsDouble(0);
                        latNW = tfMTP.getAsDouble(1);
                    } else {
                        lonNW = tfMTP.getAsDouble(2);
                        latNW = tfMTP.getAsDouble(3);
                    }

                    DMSLatLonPoint upperLeft = new DMSLatLonPoint(
                            new LatLonPoint(latNW, lonNW));
                    DMSLatLonPoint lowerRight = new DMSLatLonPoint(
                            new LatLonPoint(latNW
                                    - (yscale * (source.getHeight())), lonNW
                                    + (xscale * (source.getWidth()))));

                    System.out.println("xscale " + xscale + " yscale " +
yscale
                            + " lonNW " + lonNW + " latNW " + latNW
                            + " img height " + source.getHeight()
                            + " img width " + source.getWidth() + " "
                            + formatDMSPoint(upperLeft) + " "
                            + formatDMSPoint(lowerRight));
                    init((double) (latNW), (double) (lonNW),
                            (double) (latNW - (yscale *
(source.getHeight()))),
                            (double) (lonNW + (xscale *
(source.getWidth()))),
                            (double) xscale, (double) yscale);

                    st.close();
                } catch (Exception e) {
                    st.close();
                    System.out.println("Error reading geotiff" + e);
                    e.printStackTrace();
                    System.out
                            .println("No Geotiff referencing found.
Attempting projection file.");

                    if (imageFileName.endsWith(".tif"))
                        projfilename = imageFileName.replaceAll("\\.tif",
                                ".tfw");

                    setGeoReferencing(source, projfilename);
                }

            } else {

                if (imageFileName.endsWith(".bmp"))
                    projfilename = imageFileName.replaceAll("\\.bmp",
".bpw");
                else if (imageFileName.endsWith(".BMP"))
                    projfilename = imageFileName.replaceAll("\\.BMP",
".BPW");

                else if (imageFileName.endsWith(".JPG"))
                    projfilename = imageFileName.replaceAll("\\.JPG",
".JGW");
                else if (imageFileName.endsWith(".jpg"))
                    projfilename = imageFileName.replaceAll("\\.jpg",
".jgw");

                setGeoReferencing(source, projfilename);
            }
        } catch (IOException e) {
            System.out.println("OMJAIScalingRaster...");
            System.out.println(e.getMessage());
        }
    }

    public void setGeoReferencing(PlanarImage src, String projfilename)
            throws FileNotFoundException {
        double xscale;
        double yscale;
        double lonNW;
        double latNW;
        try {
            FileReader projectionFile;
            projectionFile = new FileReader(projfilename);
            BufferedReader reader = new BufferedReader(projectionFile);
            String line = reader.readLine();
            System.out.println("xscale=" + line);
            xscale = Double.valueOf(line).doubleValue();

            line = reader.readLine();
            line = reader.readLine();
            line = reader.readLine();
            System.out.println("yscale=" + line);
            yscale = -Double.valueOf(line).doubleValue();
            // System.out.println(line);
            line = reader.readLine();
            lonNW = Double.valueOf(line).doubleValue();
            System.out.println("lonNW=" + line);
            line = reader.readLine();
            latNW = Double.valueOf(line).doubleValue();
            System.out.println("latNW=" + line + " width=" + sourceWidth
                    + " sourceHeigth" + sourceHeigth);

            // reader.close();
            projectionFile.close();
            reader.close();

            // if (xsize == -1 || ysize == -1)
            // {
            init((double) (latNW), (double) (lonNW),
                    (double) (latNW - (xscale * (sourceHeigth))),
                    (double) (lonNW + (yscale * (sourceWidth))),
                    (double) xscale, (double) yscale);
            // }
            // else
            // init((double) (latNW), (double) (lonNW), (double) (latNW -
            // (xscale * ysize)), (double) (lonNW + (yscale * xsize)),
(double)
            // xscale, (double) yscale);

        } catch (IOException e) {
            System.out.println("setgeo :" + e.getMessage());
        }
    }

    void init(double lt, double ln, double lt_2, double ln_2, double
xim_scale,
            double yim_scale) {
        lat = (float) lt;
        lon = (float) ln;

        ximage_scale = xim_scale;
        yimage_scale = yim_scale;
        // sourcelat = lt;
        // sourcelon = lon;

        lat2 = lt_2;
        lon2 = ln_2;

        System.out.println("lat " + lat + " lon " + lon + " lat2 " + lat2
                + " lon2 " + lon2);

    }

    private String formatDMSPoint(DMSLatLonPoint p) {
        String res = "[[";
        res += p.lat_isnegative ? "-" : "";
        res += p.lat_degrees + " " + p.lat_minutes + " " + p.lat_seconds
                + "] [";
        res += p.lon_isnegative ? "-" : "";
        res += p.lon_degrees + " " + p.lon_minutes + " " + p.lon_seconds
+ "]]";
        return res;
    }
}

Don Dietrick wrote:

> HI Jason,
>
> On Sep 30, 2005, at 3:10 AM, Butler, Jason wrote:
>
>> Hello everyone,
>>
>> I have been asked by my clients to support the geotiff file format,
>> so I am attempting to develop a geotiff layer for openmap. I just
>> thought I would run my thought process past you all to check that I
>> am on the right track as I am a novice when it comes to geographic
>> terminology and maths.
>>
>> So far I have wirtten some prototype code that can load and display
>> a geotiff as a normal tiff using the jai image io code provided by
>> sun. (http://java.sun.com/products/java-media/jai/downloads/
>> download-iio-1_0_01.html) This api includes a reader for tiffs, that
>> allows access to the tiff's meta data through an xml document tree,
>> including the geotiff meta data. From the meta data tree, I have
>> been able to parse the geo tiff speciffic keys into a helper object.
>> So far so good. I should be able to get all the info I need for
>> placing the geo tiff by implementing some helper methods based on
>> the geo tiff specification. (http://www.remotesensing.org/
>> geotiff/spec/geotiffhome.html)
>
> Excellent!
>
>> My roadmap for the next few steps is
>> 1. create a Layer that takes a path to a geotiff file as a property,
>> and renders it as a raster OMGraphic at (0,0).
>> 2. extract the upper left and lower right coordinates of the tiff
>> from the meta data and render the tiff as a scaling raster omgraphic
>> within these confines.
>>
>> As far as I can tell from reading the openmap mail archives, this is
>> about where most people have gotten in past attempts. The next step is
>>
>> 3. write some code to render the geotiff directly into the current
>> openmap projection. Sounds easy, but I think this is going to be
>> difficult to implement.
>>
>> This is the bit that I would like some confirmation in my thinking.
>>
>> First I had better define a couple of terms to try and make sure I
>> am communicating clearly.
>> - Raster Space: The data in the tiff. Lets say the coordinate system
>> is I, and j and the notation R(i,,j) defines a point in the raster
>> space.
>>
>> - Geocentric Space: The coordinates on a sphere or elipsoid (like
>> the earth). The coordinate system is La (latitude) and Lo
>> (Longitude), and the notation G(La,Lo) defines a point on the elipsoid.
>>
>> - Device Space: The data being displayed in the mapbean. Lets say
>> the coordinate system is X and Y and the notation D(x,y) defines a
>> point on the screen in the mapbean (or a layer if you want).
>>
>> - Projection: A function P the maps G(La,Lo) -> D(x,y). I am not
>> sure that this function is always reversible. Does anyone know if
>> this function is always reversible?
>
> In OpenMap, the Projection is an Object that translates coordinates
> from your Geocentric Space to your Device Space (projection.forward
> (lat, lon)). The function is reversible, called with the inverse()
> function (projection.inverse(x, y). The MapBean's main
> responsibility is to manage the current Projection object and make
> sure the active Layers get a projection notification when it changes.
>
>> The main problem is we need to convert the tiff from its current
>> coordinate sytem (Raster Space) into Device Space. At the moment I
>> am thinking that perhaps the best approach is to initally convert
>> the tiff into Geocentric space and then buffer this for between
>> projection changes. This raises the question about what is the best
>> grainularity for the lat and longs in the buffer, but that should be
>> easy to adjust based on parameters. I am not so sure how easy it is
>> to actually do the conversion from raster space to geocentric space.
>> Any helpful pointers for resources on this would be most welcome.
>>
>> Then we just have to render the geocentric buffer into device
>> space. There are two possible approaches to this.
>
> It really depends on the projection of the image in Raster Space,
> which may be different for every image. If you automatically convert
> all images to be re-projected into Geocentric Space, then all the
> layer has to do after that is the same operation for all images. You
> probably don't want to hold the original image data in memory after
> the first conversion.
>
> Depending on the size of the original images, you may want to tile
> the geocentric images and manage them separately.
>
> The com.bbn.openmap.plugin.earthImage.EarthImagePlugIn is an brute-
> force example of converting images that cover the entire earth
> (-180/180, -90/90, Mercator Projection) to the current OpenMap
> projection. It takes a pixel-by-pixel approach for the projection
> image space, inverse projection the pixel location to find the lat/
> lon location, and then going into the image to get the pixel color
> for that lat/lon (which is why it only works for Mercator projections
> with the total extents, it assumes these dimensions in order to do
> that pixel color lookup). It's not all *that* slow, and it
> automatically determines the granularity needed for the given
> projection.
>
> The other thing that's on my list to try out is to use the
> javax.media.jai.WarpGrid class in JAI to do some of the re-
> projecting, using values set by the projection to do translation for
> the entire image.
>
>
> Hope this helps,
>
> Don
>
>> A) If P is always reversible, then whenever a projectionChange event
>> occurs I should be able to draw the tiff by doing a reverse mapping
>> from D(x,y) to G(la,lo), sampling at each D(x,y). The problems I can
>> see with this is that it may be slow. (Have to sample each and every
>> pixel in the bounding box each time a projection changes) Also
>> D(x,y) may map to more than one R(i,,j), so may need to code for
>> multiple samples per pixel to avoid aliasing artifacts, etc.
>> Especially as scaling occurs. The more samples per pixel, the slower
>> things are going to be.
>>
>> B) Otherwise, I will just have to loop though the Geocentric buffer
>> and build up a picture that way.
>>
>>
>>
>> Have I missed anything out so far in my thinking? Any comments or
>> help you can give me is greatly appreciated.
>>
>> Best regards,
>>
>> Jason Butler.
>>
>>
>>
>
>
>
> =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
> Don Dietrick, dietrick_at_bbn.com
> BBN Technologies, Cambridge, MA
> =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
>
>
> --
> [To unsubscribe to this list send an email to "majdart_at_bbn.com"
> with the following text in the BODY of the message "unsubscribe
> openmap-users"]

--
[To unsubscribe to this list send an email to "majdart_at_bbn.com"
with the following text in the BODY of the message "unsubscribe openmap-users"]
Received on Wed Oct 05 2005 - 05:19:20 EDT

This archive was generated by hypermail 2.3.0 : Tue Mar 28 2017 - 23:25:07 EDT