package uk.ac.leeds.ccg.raster; import uk.ac.leeds.ccg.geotools.*; import uk.ac.leeds.ccg.widgets.*; import java.lang.*; import java.awt.*; import java.io.*; import java.net.*; import java.util.*; import java.awt.image.*; /** * @author * Ian Turton Centre for * Computaional Geography, University of Leeds, LS2 9JT, 1998.
* i.turton@geog.leeds.ac.uk **/ public class RasterLayer extends uk.ac.leeds.ccg.geotools.SimpleLayer implements Serializable,ChangedListener { static final boolean debug=false; // set true for al sorts of debuging info boolean grid=false; // set true to turn on a grid /** * The geographic origin of the layer */ private GeoPoint origin; /** * the raster to be drawn */ protected Raster r; /** * the image representation of the raster */ Image image; /** * The data used to produce the image */ private int data[]; public RasterLayer(){ if(debug)System.out.println("---->uk.ac.leeds.ccg.raster.RasterLayer constructed. Will identify itself as RaL->"); r=null; } public RasterLayer(Raster ras) { if(debug)System.out.println("---->uk.ac.leeds.ccg.raster.RasterLayer constructed. Will identify itself as RaL->"); r=ras; if(ras!=null) r.addChangedListener(this); } /** * Open a raster from the ascii grid file named. * @param name the file name of the ascii grid file * @exception java.io.IOException if anything goes wrong when opening the * file * @see uk.ac.leeds.ccg.raster.Raster */ public RasterLayer(String name) throws java.io.IOException{ if(debug)System.out.println("---->uk.ac.leeds.ccg.raster.RasterLayer constructed. Will identify itself as RaL->"); set=false; r=new Raster(name); r.addChangedListener(this); } public RasterLayer(Image im, int width, int height, GeoRectangle gr){ if(debug)System.out.println("---->uk.ac.leeds.ccg.raster.RasterLayer constructed. Will identify itself as RaL->"); r = new Raster(im,width,height,gr); set=false; r.addChangedListener(this); } public void newData(int h,int w){ data = new int[(h)*(w)]; } public Raster getRaster(){ return r; } public void setRaster(Raster rr){ r=rr; set=false; r.addChangedListener(this); notifyLayerChangedListeners(LayerChangedEvent.GEOGRAPHY); } /** * Open a raster based on the centres of the circles * @param an array of GeoCircles * @see uk.ac.leeds.ccg.raster.Raster */ public RasterLayer(GeoCircle[] circles){ r=new circleRaster(circles); set=false; r.addChangedListener(this); } /** * Open a raster based on the values of the circles * @param an array of GeoCircles * @param size the cellsize of the raster in geographic coordinates * @param in the data to be rasterized one value per circle * @see uk.ac.leeds.ccg.raster.Raster */ public RasterLayer(GeoCircle[] circles,double size,double in[]){ r=new circleRaster(circles,size,in); set=false; r.addChangedListener(this); } /** * Open a raster based on the values of the circles * @param an array of GeoCircles * @param size the cellsize of the raster in geographic coordinates * @param in the data to be rasterized one value per circle * @param m mapextent to be used * @see uk.ac.leeds.ccg.raster.Raster */ public RasterLayer(GeoCircle[] circles,double size,double in[],GeoRectangle m){ r=new circleRaster(circles,size,in,m); set=false; r.addChangedListener(this); } /** * paints a highlight
* does nothing at the moment since I can't decide what a highlit raster * means. */ public void paintHighlight(Graphics g, Scaler s,int id,ShadeStyle style){ // nothing } /** * @return the bounding box of the raster contained in the layer */ public GeoRectangle getBounds(){ if(r!=null) return(r.getBounds()); else return(new GeoRectangle()); } public void paintScaled(GeoGraphics g){ paintScaled(g.getGraphics(),g.getScale(),g.getShade(),g.getData(),g.getStyle()); } Shader lastshader = null; Vector colors = new Vector(); Vector values = new Vector(); /** * paints the relevant area of the raster to the viewer * @param g the graphics layer to be drawn to * @param scale the scale to draw at * @param shade the shader to colour the raster with.
* If this is null a * default shading from black to red will be used. 0=black, 255+ = red * @deprecated */ private int off=0,dw,dh; private boolean set = false ; // is the data layer set public void paintScaled(Graphics g, Scaler scale, Shader shade, GeoData dat,ShadeStyle style){ int h,w; Canvas obs = new Canvas(); MemoryImageSource source; GeoRectangle gr = scale.getMapExtent(); if(debug)System.out.println("RaL->gr "+gr); GeoRectangle me = getBounds(); if(debug)System.out.println("RaL->me "+me); // first see if we are on screen GeoRectangle out = gr.createIntersect(me); //if(debug)System.out.println("RaL->gr "+gr+"\nme "+me+"\nout "+out); if(out!=null){ // we're on screen so do something if(debug)System.out.println("RaL->out "+out.getX()+" "+out.getY()); // first adjust our output origin to be a cell boundary double cellsize=r.getCellSize(); if(!set){ // this is a first draw for this raster int here[]; int pt=0; if(debug)System.out.println("RaL->out "+out); if(debug)System.out.println("RaL->x:"+((out.x-me.x)/cellsize)+" "+(out.height/cellsize)); data = new int[r.getHeight()*r.getWidth()]; h=r.getHeight(); w=r.getWidth(); dw=w; dh=h; off=0; // set up the underlying image data for (int i=0;iPainted "+(pt)+" pixels"); //set=true; } /* to prevent jumping we need the output area to start and end on a * cell boundary. Thus we move the out rectangle origin down and to * the left and adjust the width and height so we only draw whole * cells even if the viewer doesn't see all of them */ double newy1 = ((int)Math.floor(out.getY()/cellsize))*cellsize; double newx1 = ((int)Math.floor(out.getX()/cellsize))*cellsize; double newy2 = ((int)Math.ceil((out.getY()+out.getHeight())/cellsize))*cellsize; double newx2 = ((int)Math.ceil((out.getX()+out.getWidth())/cellsize))*cellsize; if(debug)System.out.println("RaL->out "+out); /* now we need to check we haven't actually moved beyond the extent * of the raster */ if(newx1too left "+newx1+" -> "+me.x); newx1=me.x; } if(newy1too low "+newy1+" -> "+me.y); newy1=me.y; } if(newx2>(me.x+me.width)){ if(debug)System.out.println("RaL->too wide "+newx2+" -> "+(me.x+me.width)); newx2=me.x+me.width; } if(newy2>(me.y+me.height)) { if(debug)System.out.println("RaL->too high "+newy2+" -> "+(me.y+me.height)); newy2=me.y+me.height; } out=new GeoRectangle(); out.add(newx1,newy1); out.add(newx2,newy2); if(debug)System.out.println("RaL->new "+out); if(debug)System.out.println("RaL->new x "+(out.x/cellsize)); if(debug)System.out.println("RaL->new y "+(out.y/cellsize)); if(debug)System.out.println("RaL->raster paint part short cut"); // now find offset into the data array and calc new width and height w=(int)Math.round((out.width/cellsize)); h=(int)Math.round(((out.height)/cellsize)); if(debug)System.out.println("RaL->width "+w+" "+out.width+" "+(out.width/cellsize)); if(debug)System.out.println("RaL->height "+h+" "+out.height+" "+(out.height/cellsize)); int startx = r.getCellCol(out.x); int starty = r.getCellRow(out.y+out.height); if(debug)System.out.println("RaL->offsets "+startx+" "+(starty)+" "+out.y+" "+out.height); // now draw the data if(debug)System.out.println("RaL->w/h 1"+w+" "+h); /* in theory these never get used and could be deleted but I'm * paranoid! */ if(h>r.getHeight()){ if(debug)System.out.println("RaL->too high 2"); h=r.getHeight(); } if(w>r.getWidth()){ if(debug)System.out.println("RaL->too wide 2"); w=r.getWidth(); } if(startx<0){ if(debug)System.out.println("RaL->too left 2"); startx=0; } if(starty<0){ if(debug)System.out.println("RaL->too low 2"); starty=0; } /* given an offset and output width we can use memoryimagesource to * do the actual drawing */ off= (int)((starty)*dw+startx); /* the following works arround a bug(?) in netscape 4.05 where it * crashes if you try to draw part of the array in the bottom R * corner. Since it doesn't do this in Appletviewer or ME IE4/5 * I blame netscape and work round it, the problem may be in my code * or my understanding of how memoryimage source handles offsets */ if(debug)System.out.println("RaL->dw "+dw+" "+w+" "+(dw-w)); if(debug)System.out.println("RaL->dh "+dh+" "+h+" "+(dh-h)); int first_offset = dw*(dh-h); // the whole block at the top int second_offset = (dw-w)*h; // the bit down the left side int out_bit = w*h; // the actual image to draw // the sum of these three bits must be less than the total image if(debug)System.out.println("RaL->off "+first_offset+" "+second_offset+" "+ out_bit+" -> "+(first_offset+second_offset+out_bit)+" "+data.length); if(first_offset+second_offset+out_bit>data.length){ return;} // this is bad /* It seems that this netscape fix breaks other vms - what do I know*/ //if(first_offset+second_offset+out_bit==data.length){ h--;} // this is netscape if(debug)System.out.println("RaL->offsets "+startx+" "+(starty)+" "+off); if(debug)System.out.println("RaL->w/h 2"+w+" "+h); if(debug)System.out.println("RaL->data "+data.length+" off:"+off+" "+(w*h)+" "+(off+w*h)); source = new MemoryImageSource(w,h,data,off,dw); image = obs.createImage(source); /* now we need to know where to draw the image, I think that this can * cause a slight jump to the output raster as we're moving from a * double to the nearest int pixel position but thats life! */ int origin[]=scale.toGraphics(out.x,out.y); int gh=scale.toGraphics(out.height); int gw=scale.toGraphics(out.width); if(debug)System.out.println("RaL->out "+out.x+" "+out.y); if(debug)System.out.println("RaL-> -->"+origin[0]+" "+origin[1]+" "+(origin[1]-gh)); // boolean fred = g.drawImage(image,origin[0],origin[1]-gh,obs); // we should use image.getScaledInstance here boolean fred = g.drawImage(image,origin[0],origin[1]-gh, gw,gh,obs); if(debug)g.setColor(Color.yellow); if(debug)g.drawRect(origin[0],origin[1]-gh,gw,gh); if(grid){ for(int i = origin[0];iPainted "+(pt)+" pixels"); set=false; source = new MemoryImageSource(w,h,data,off,w); image = obs.createImage(source); return image; } /** Getter for property grid. * @return Value of property grid. */ public boolean isGrid() { return grid; } /** Setter for property grid. * @param grid New value of property grid. */ public void setGrid(boolean grid) { this.grid = grid; } } /* * $Log: RasterLayer.java,v $ * Revision 1.8 2002/04/02 17:00:46 ianturton * added setter/getter method for grid - allows user to draw a gridColored grid over the raster * * Revision 1.7 2001/08/01 12:32:46 ianturton * modification submited by Michael Becke to reduce * memory usage. * * * */