Added by jgarnett, last edited by jgarnett on Nov 25, 2007  (view change)

Labels

 
(None)

This lab ties in with the excellent raster support offered by our coverage module; If you are looking for some sample data to try out with this program please download the one from uDig Walkthrought 1 it includes a clouds and earthlights rasters in "world plus image" format.

Dependencies

The following dependencies are needed for this lab:

<dependency>
  <groupId>org.geotools</groupId>
  <artifactId>gt2-image</artifactId>
  <version>${project.version}</version>
</dependency>

You will also need to Java Advanced Imaging and Image IO around to do raster processing:

  • Install JAI and ImageIO into your JRE
  • Or add a pure jar dependency as follows:
    <dependency>
            <groupId>javax.media</groupId>
            <artifactId>jai_core</artifactId>
            <version>1.1.3</version>
          </dependency>
          <dependency>
            <groupId>javax.media</groupId>
            <artifactId>jai_codec</artifactId>
            <version>1.1.3</version>
          </dependency>
          <dependency>
            <groupId>javax.media</groupId>
            <artifactId>jai_imageio</artifactId>
            <version>1.1</version>
          </dependency>

Loading a GridCoverage

The sample program starts out responsible for three things:

  • Prompting us for a World plus Image File
  • Loading the file as GridCoverage
  • Opening up a Dialog
package org.geotools.demo;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.File;
import java.io.FileNotFoundException;

import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
import javax.swing.filechooser.FileFilter;

import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.gce.image.WorldImageReader;
import org.geotools.geometry.DirectPosition2D;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.renderer.lite.gridcoverage2d.GridCoverageRenderer;
import org.geotools.styling.RasterSymbolizer;
import org.geotools.styling.RasterSymbolizerImpl;
import org.opengis.geometry.DirectPosition;

public class ImageLab extends JDialog {
	/** Original coverage we are working on */
	GridCoverage2D coverage;
	ReferencedEnvelope bbox;
	CoveragePanel panel;

	/**
	 * Explore the functionality of the provided GridCoverage (think BufferedImage + CRS).
	 * <p>
	 * A GridCoverage literally a set of features that "covers"
	 * an area without gaps; in the case of grid coverage the area
	 * is covered by an regular grid.
	 * <p>
	 * Coverage work by letting you call a "sample" operation in order
	 * to retrieve a Record of the data at the location. A grid coverage
	 * lets you express the location using row and column.
	 * <p> 
	 * @param coverage
	 */
	public ImageLab( GridCoverage2D coverage ){
		setModal(true);
		setResizable(false);
		this.coverage = coverage;
		this.bbox = new ReferencedEnvelope( coverage.getEnvelope() );
		this.panel = new CoveragePanel();
		add( panel );
		pack();
	}

	public static void main( String[] args ) throws Exception {
		File file = getImageFile( args );
		WorldImageReader reader = new WorldImageReader( file );
		GridCoverage2D coverage = (GridCoverage2D) reader.read(null);		
		
		ImageLab imageLab = new ImageLab( coverage );
		imageLab.setVisible(true);
		System.exit(0);
	}
	
	private static File getImageFile(String[] args) throws FileNotFoundException {
		File file;
		if (args.length == 0) {
			JFileChooser chooser = new JFileChooser();
			chooser.setDialogTitle("Open Image file");
			chooser.setFileFilter(new FileFilter() {
				public boolean accept(File f) {
					return f.isDirectory() || f.getPath().endsWith("jpg")
							|| f.getPath().endsWith("JPG");
				}

				public String getDescription() {
					return "Comma Seperated Value";
				}
			});
			int returnVal = chooser.showOpenDialog(null);

			if (returnVal != JFileChooser.APPROVE_OPTION) {
				System.exit(0);
			}
			file = chooser.getSelectedFile();

			System.out.println("Opening Image file: " + file.getName());
		} else {
			file = new File(args[0]);
		}
		if (!file.exists()) {
			throw new FileNotFoundException(file.getAbsolutePath());
		}
		return file;
	}
}

Rendering a GridCoverage

The above code is missing a bit; the part that draws a GridCoverage. We are going to put together and example "CoveragePanel" with an overrride for paintComponent( graphics ).

class CoveragePanel extends JPanel {
		CoveragePanel(){
			setBackground( Color.WHITE );
		}
		public Dimension getPreferredSize() {
			ReferencedEnvelope world = new ReferencedEnvelope( coverage.getEnvelope() );
			double ratio = world.getHeight() / world.getWidth();
			double w = 640.0;
			double h = 640.0 * ratio;
			return new Dimension( (int) w, (int) h );
		}
		
		public void paintComponent(Graphics graphics) {
			super.paintComponents( graphics );			
			Graphics2D g = (Graphics2D) graphics;			
			GridCoverageRenderer renderer;
			try {
				renderer = new GridCoverageRenderer( coverage.getCoordinateReferenceSystem(), bbox, getBounds() );
				renderer.paint( g, coverage, style );
			} catch (Exception e) {
				g.drawString( e.getLocalizedMessage(), 0, getHeight() / 2 );
			}
		}
	}

Zoom

The following class is a very quick hack; just so you can change your bounds visually. Add the following to your constructor:

Zoom zoom;

	/**
	 * Explore the functionality of the provided GridCoverage (think BufferedImage + CRS).
	 * <p>
	 * A GridCoverage literally a set of features that "covers"
	 * an area without gaps; in the case of grid coverage the area
	 * is covered by an regular grid.
	 * <p>
	 * Coverage work by letting you call a "sample" operation in order
	 * to retrieve a Record of the data at the location. A grid coverage
	 * lets you express the location using row and column.
	 * <p> 
	 * @param coverage
	 */
	public ImageLab( GridCoverage2D coverage ){
		setModal(true);
		setResizable(false);
		this.coverage = coverage;
		this.bbox = new ReferencedEnvelope( coverage.getEnvelope() );
		this.panel = new CoveragePanel();
		add( panel );
		
		this.zoom = new Zoom();
		setGlassPane( zoom );
		zoom.setVisible(true);		
		pack();
	}

And paste in this quick utility class - this code is not a good example of how to figure out mouse clicks (the RenderingUtility screenToWorld transformation facilities should be used):

/**
	 * Take the clicks and update the bbox; implemented as a glass pane
	 * so we do not have to redraw the image.
	 */
	class Zoom extends JComponent implements MouseListener, MouseMotionListener {
		Point point1;
		Point point2;		
		Zoom(){
			addMouseListener( this );
			addMouseMotionListener( this );
		}
		public void mouseClicked(MouseEvent squeek) {
			if( squeek.getClickCount() == 2 ){
				bbox = new ReferencedEnvelope( coverage.getEnvelope2D() );
				point1 = null;
				point2 = null;
			}
		}
		public void mouseEntered(MouseEvent squeek) {			
		}
		public void mouseExited(MouseEvent squeek) {			
		}
		public void mousePressed(MouseEvent squeek) {
			point1 = squeek.getPoint();
			repaint();
		}
		public void mouseReleased(MouseEvent squeek) {
			if( point1 != null && point2 != null &&
		        point1.distance(point2) > 5 ){
				DirectPosition2D location1 = toLocation( point1 );
				DirectPosition2D location2 = toLocation( point2 );
				if( location1 == null || location2 == null ) return;

				bbox = aspectRatioFix( location1, location2 );
				panel.repaint();
			}
			point1 = null;
			point2 = null;
			repaint();
		}
		
		public void mouseDragged(MouseEvent squeek) {			
			if( point1 != null ){
				point2 = squeek.getPoint();
				repaint();
			}
		}
		public void mouseMoved(MouseEvent squeek) {			
			
		}
		protected void paintComponent(Graphics g) {
			if( point1 != null && point2 != null ){
				Rectangle feedback = new Rectangle( point1 );
				feedback.add( point2 );
				
				g.setColor( Color.BLUE );
				g.drawRect( feedback.x, feedback.y, feedback.width, feedback.height );
				
				g.setColor( new Color( 0.0f, 0.0f, 1.0f,0.5f));
				g.fillRect(feedback.x, feedback.y, feedback.width, feedback.height );
			}			
		}
		public DirectPosition2D toLocation(Point point) {
			double w = (double) getWidth();
			double h = (double) getHeight();			
			double rx = ((double) point.x) / w;
			double ry = (h-(double) point.y) / h;
			
			DirectPosition start = bbox.getLowerCorner();
			DirectPosition2D location = new DirectPosition2D( coverage.getCoordinateReferenceSystem() );
			location.x = start.getOrdinate(0) + rx*bbox.getWidth();
			location.y = start.getOrdinate(1) + ry*bbox.getHeight();
			
			return location;
		}
		public ReferencedEnvelope aspectRatioFix( DirectPosition start, DirectPosition end ){
			ReferencedEnvelope request = new ReferencedEnvelope( coverage.getCoordinateReferenceSystem() );
			request.include( start.getOrdinate(0), start.getOrdinate(1) );
			request.include( end.getOrdinate(0), end.getOrdinate(1) );
			start = request.getLowerCorner();
			end = request.getUpperCorner();
			
			ReferencedEnvelope world = new ReferencedEnvelope( coverage.getEnvelope() );
			double ratio = world.getHeight() / world.getWidth();
			
			double w = request.getWidth();
			double h = request.getWidth() * ratio;
			
			ReferencedEnvelope envelope = new ReferencedEnvelope(coverage.getCoordinateReferenceSystem());
			envelope.include( start.getOrdinate(0), start.getOrdinate(1) );
			envelope.include( start.getOrdinate(0)+w, start.getOrdinate(1)+h );
			return envelope;
		}
	}

Have such error in eclipse

The type javax.media.jai.PropertySource cannot be resolved. It is
 indirectly referenced from required .class files

on string: GridCoverage2D coverage1 = (GridCoverage2D) reader.read(null); 

I have modified code to use without cast: GridCoverage coverage = reader.read(null); Eclipse's error disapiared, but during opening any jpg file it drope error:

Exception in thread "main" java.lang.NoClassDefFoundError: javax/media/jai/PlanarImage

on getting coverage above. How can I solve it? Did I miss some dependencies?

Posted by merzod at Oct 30, 2007 03:40

To solve this problem I'v downloaded jai-1_1_4 library and added 3 jars from ext folder into classpath. And now I have new problem:

Exception in thread "main" java.lang.IllegalArgumentException: ImageRead: No OperationDescriptor is registered in the current operation registry under this name. 

Posted by merzod at Oct 30, 2007 04:37

I have the same problem Unfortunately there are a lot of GeoTools examples that are not working right after copy&paste to my IDE.

I'm on Mac OS X with GeoTools 2.4-RC0

java.lang.IllegalArgumentException: ImageRead: No OperationDescriptor is registered in the current operation registry under this name 

Hey guys; you should be able to add the jai stuff as a dependency in your pom.xml. There is really only two good ways to do kind of stuff; use maven to ensure you have all the needed software to run, or follow some instructions like Welcome to Eclipse Developers that dumps everything into your IDE.

Talking to the geotools users list yielded the following advice from Jesse Eichar:

Hi,

You can work on OSX. But you have to download the Linux Image IO and install it manually. It has the Image read.

So the following Manual Install links should be good advice: