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; } }
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.
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.
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?