Statistics
| Revision:

root / branches / Mobile_Compatible_Hito_1 / libFMap / src / es / prodevelop / gvsig / mobile / fmap / driver / raster / memory / wkformat / WKFRasterDriver.java @ 21606

History | View | Annotate | Download (17.1 KB)

1
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
2
 *
3
 * Copyright (C) 2006 Prodevelop and Generalitat Valenciana.
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
18
 *
19
 * For more information, contact:
20
 *
21
 *   Generalitat Valenciana
22
 *   Conselleria d'Infraestructures i Transport
23
 *   Av. Blasco Ib??ez, 50
24
 *   46010 VALENCIA
25
 *   SPAIN
26
 *
27
 *   +34 963862235
28
 *   gvsig@gva.es
29
 *   http://www.gvsig.gva.es
30
 *
31
 *    or
32
 *
33
 *   Prodevelop Integraci?n de Tecnolog?as SL
34
 *   Conde Salvatierra de ?lava , 34-10
35
 *   46004 Valencia
36
 *   Spain
37
 *
38
 *   +34 963 510 612
39
 *   +34 963 510 968
40
 *   gis@prodevelop.es
41
 *   http://www.prodevelop.es
42
 *
43
 *    or
44
 *
45
 *   Instituto de Rob?tica
46
 *   Apartado de correos 2085
47
 *   46071 Valencia
48
 *   (Spain)
49
 *   
50
 *   +34 963 543 577
51
 *   jjordan@robotica.uv.es
52
 *   http://robotica.uv.es
53
 *   
54
 */
55

    
56
package es.prodevelop.gvsig.mobile.fmap.driver.raster.memory.wkformat;
57

    
58
import java.awt.Graphics2D;
59
import java.awt.Image;
60
import java.awt.geom.AffineTransform;
61
import java.awt.geom.Point2D;
62
import java.awt.geom.Rectangle2D;
63
import java.awt.image.BufferedImage;
64
import java.awt.image.DataBuffer;
65
import java.io.File;
66
import java.io.IOException;
67

    
68
import org.apache.log4j.Logger;
69

    
70
import es.prodevelop.gvsig.mobile.fmap.driver.FMapDriverException;
71
import es.prodevelop.gvsig.mobile.fmap.driver.raster.GeorreferencedRasterDriver;
72
import es.prodevelop.gvsig.mobile.fmap.proj.IProjection;
73
import es.prodevelop.gvsig.mobile.fmap.util.ResourceReader;
74
import es.prodevelop.gvsig.mobile.fmap.util.Utils;
75
import es.prodevelop.gvsig.mobile.fmap.viewport.ViewPort;
76

    
77
/**
78
 * Raster driver for well known image formats (PNg, JPG, GIF). These images
79
 * have to be loaded completely in memory, so the size is limited to a few hundred
80
 * thousands pixels.
81
 * 
82
 * @see java.awt.geom.AffineTransform
83
 * 
84
 * @author jldominguez
85
 *
86
 */
87
public class WKFRasterDriver implements GeorreferencedRasterDriver {
88
        
89
        private static final int WKRASTER_MAX_PIXEL_COUNT = 500000;
90
        private static Logger logger = Logger.getLogger(WKFRasterDriver.class);
91
        
92
        private IProjection proj = null;
93
        private int trans = 0;
94
        private AffineTransform at = AffineTransform.getTranslateInstance(0, 0);
95
        private ViewPort viewPort;
96
        private Rectangle2D extent = null;
97
        
98
        private int imgWidth = 1;
99
        private int imgHeight = 1;
100
        private String path = "";
101
        
102
        private byte[] imageFileBytes = null;
103

    
104
        /**
105
         * Constructor.
106
         * 
107
         * @param vp current view port, used for geographic computations
108
         */
109
        public WKFRasterDriver(ViewPort vp) {
110
                viewPort = vp;
111
        }
112

    
113
        /**
114
         * Initializes  the driver. Simply sets the projection.
115
         * 
116
         * @param p the current projection
117
         */
118
        public void initialize(IProjection p) throws IOException {
119
                proj = p;
120
        }
121

    
122
        /**
123
         * @return the current projection
124
         */
125
        public IProjection getProjection() {
126
                return proj;
127
        }
128

    
129
        /**
130
         * Sets a new projection
131
         * 
132
         * @param p the new projection to be set.
133
         */
134
        public void setProjection(IProjection p) {
135
                proj = p;
136
        }
137

    
138
        /**
139
         * @return the full extent of the raster
140
         */
141
        public Rectangle2D getFullExtent() {
142
                return extent;
143
        }
144

    
145
        /**
146
         * @return whether this raster is georrefernced. Returns true.
147
         */
148
        public boolean isGeoreferenced() {
149
                return true;
150
        }
151

    
152
        /**
153
         * Sets a new affine transformation. 
154
         * @param t the new affine transform
155
         */
156
        public void setAffineTransform(AffineTransform t) {
157
                // at = t;
158
                logger.warn("Someone tried to set WKR raster driver AffineTransform (?)");
159
        }
160

    
161
        /**
162
         * @return the current affine transform
163
         */
164
        public AffineTransform getAffineTransform() {
165
                return at;
166
        }
167

    
168
        /**
169
         * @return whether the provided file object is acceptable as an input raster file.
170
         */
171
        public boolean fileAccepted(File fName) {
172
                
173
                String aux = fName.getAbsolutePath();
174
                int l = aux.length();
175
                aux = aux.substring(l - 4, l);
176
                if (aux.compareToIgnoreCase("jpeg") == 0) return true;
177
                aux = aux.substring(l - 3, l);
178
                if (aux.compareToIgnoreCase("jpg") == 0) return true;
179
                if (aux.compareToIgnoreCase("png") == 0) return true;
180
                if (aux.compareToIgnoreCase("gif") == 0) return true;
181
                return false;
182
        }
183
        
184
        private Image getImageFromBytes(byte[] bb) throws IOException {
185
                // Image resp = Toolkit.getDefaultToolkit().createImage(bb);
186
                Image resp = ResourceReader.getImage(bb);
187
                
188
                if (resp == null) throw new IOException("Did not read image (img = null)");
189
                if (resp.getWidth(null) == -1) throw new IOException("Did not read image (w/h = -1)");
190
                if ((resp.getWidth(null)  * resp.getHeight(null)) > WKRASTER_MAX_PIXEL_COUNT) {
191
                        throw new IOException("Image is too large (>" +
192
                                        Utils.getFormattedDouble(0.000001 * WKRASTER_MAX_PIXEL_COUNT, 2) +
193
                                        "Megapixels)");
194
                }
195
                return resp;
196
        }
197
        
198
        /**
199
         * Opens the specified file and expands it complete in memory. This is the biggest issue
200
         * with this type of image files (png, jpg, gif). Their size is very limited
201
         * because they cannot be accessed for a limited zone of interest.
202
         * 
203
         * @param f the raster file to be opened
204
         */
205
        public void open(File f) throws IOException {
206
                try {
207
                        imageFileBytes = Utils.readFileIntoByteArray(f);
208
                        logger.debug("byte[] file_bytes.length = " + imageFileBytes.length);
209

    
210
                        Image wholeImage = getImageFromBytes(imageFileBytes);
211
                        imgWidth = wholeImage.getWidth(null);
212
                        imgHeight = wholeImage.getHeight(null);
213
                        
214
                        logger.debug("imgWidth = " + imgWidth);
215
                        logger.debug("imgHeight = " + imgHeight);
216
                        wholeImage = null;
217
                        System.gc();
218

    
219
                        path = f.getAbsolutePath();
220
                        int lastp = path.lastIndexOf(".");
221
                        String ext = path.substring(lastp + 1, path.length());
222
                        String[] valid = getWorldFileExtensions(ext);
223
                        at = Utils.readWldFile(f, valid);
224
                        extent = Utils.getExtent(at, imgWidth, imgHeight);
225
                        
226
                } catch (IOException ex) {
227
                        logger.error("While opening WK raster file: " + ex.getMessage());
228
                        throw new IOException(ex.getMessage());
229
                }
230
        }
231

    
232
        /**
233
         * Initializes the driver. Right now it's an empty method.
234
         * 
235
         */
236
        public void initialize() throws IOException {
237
        }
238

    
239
        /**
240
         * Closes the driver. Right now it's an empty method.
241
         */
242
        public void close() throws IOException {
243
        }
244

    
245
        /**
246
         * @return the number of bands in this raster.
247
         */
248
        public int getNumBands() {
249
                return 3;
250
        }
251

    
252
        /**
253
         * @return the current transparency level of this image 
254
         */
255
        public int getTransparency() {
256
                return trans;
257
        }
258

    
259
        /**
260
         * Sets the transparency level of this image
261
         * @param t the new transparecncy level
262
         */
263
        public void setTransparency(int t) {
264
                trans = t;
265
        }
266

    
267
        /**
268
         * Draws the image. This method tries to behave in the less memory-consuming way.
269
         * 
270
         * If the view is larger than the image, the image is resampled down. If
271
         * the image is larger than the view, a new buffered image is created , then the image
272
         * is painted on it (clipping), then the buffered image is resampled up.
273
         * 
274
         * @param g the graphics object where the image has to be painted
275
         * @param viewPort the current viewport.
276
         * 
277
         */
278
        public void draw(Graphics2D g, ViewPort viewPort)
279
                        throws FMapDriverException {
280
                
281
                Rectangle2D vext = viewPort.getAdjustedExtent();
282
                if (! Utils.rectanglesItersect(vext, extent)) {
283
                        return;
284
                }
285
                
286
                int[] raster_transf = getRasterTansformation(
287
                                vext,
288
                                extent, viewPort.getImageWidth(), imgWidth, imgHeight);
289
                
290
                for (int i=0; i<raster_transf.length; i++) {
291
                        logger.debug("raster_transf[" + i + "] = " + raster_transf[i]); 
292
                }
293

    
294
                if (raster_transf == null) return;
295
                
296
                Image wholeImage = null;
297
                
298
                try {
299
                        wholeImage = getImageFromBytes(imageFileBytes);
300
                } catch (IOException e) {
301
                        throw new FMapDriverException(e.getMessage());
302
                }
303

    
304
                if (raster_transf.length == 8) {
305
                        // zoom in
306
                        logger.debug("DRAW WK RASTER: CASE 1: ZOOM IN:");
307
                        BufferedImage bim = Utils.createBufferedImage(raster_transf[2], raster_transf[3]);
308
                        
309
                        if ((bim == null) || (bim.getWidth(null) != raster_transf[2])) {
310
                                throw new FMapDriverException("Could not instantiate buff image.");
311
                        }
312

    
313
                        bim.getGraphics().drawImage(wholeImage, -raster_transf[0], -raster_transf[1], null);
314
                        logger.debug("Scale up: " + (raster_transf[4]) + ", " + (raster_transf[5]));
315
                        try {
316
                                g.drawImage(
317
                                        bim.getScaledInstance(
318
                                                        raster_transf[4],
319
                                                        raster_transf[5],
320
                                                        Image.SCALE_FAST),
321
                                        raster_transf[6], raster_transf[7], null);
322
                        } catch (Throwable th) {
323
                                logger.error("While resampling image: " + th.getMessage());
324
                                bim = null;
325
                                throw new FMapDriverException(th.getMessage());
326
                        }
327
                        bim = null;
328
                } else {
329
                        
330
                        Rectangle2D interse = new Rectangle2D.Double();
331
                        Rectangle2D.intersect(vext, extent, interse);
332
                        
333
                        int bi_w = Math.round( (float) ((interse.getWidth() / extent.getWidth()) * imgWidth));
334
                        int bi_h = Math.round( (float) ((interse.getHeight() / extent.getHeight()) * imgHeight));
335
                        
336
                        // decision:
337
                        // (whole raster resized down) vs. (zone of interest resized up)
338
                        int whole_raster_resized_down = raster_transf[2] * raster_transf[3];
339
                        int zone_of_interest_resized_up = bi_w * bi_h;
340
                        logger.debug("Decision: ");
341
                        logger.debug("whole_raster_resized_down:  = " + whole_raster_resized_down);
342
                        logger.debug("zone_of_interest_resized_up: = " + zone_of_interest_resized_up);
343
                        
344
                        // raster_transf.length == 4
345
                        // downsample whole raster (to small size), then paint
346
                        if (whole_raster_resized_down < zone_of_interest_resized_up) {
347
                                
348
                                logger.debug("DRAW WK RASTER: CASE 2: ZOOM OUT, RESAMPLE WHOLE TO SMALL IMAGE");
349

    
350
                                logger.debug("Resample whole to: " + raster_transf[2] + ", " + raster_transf[3]);
351
                                Image smallImage = wholeImage.getScaledInstance(
352
                                                raster_transf[2],
353
                                                raster_transf[3],
354
                                                Image.SCALE_FAST);
355
                                if ((smallImage == null) || (smallImage.getWidth(null) != raster_transf[2])) {
356
                                        throw new FMapDriverException("Could not resample image (down).");
357
                                }
358
                                
359
                                logger.debug("Final drarw, offset: " + raster_transf[0] + ", " + raster_transf[1]);
360
                                g.drawImage(smallImage, raster_transf[0], raster_transf[1], null);
361
                        } else {
362
                                
363
                                logger.debug("DRAW WK RASTER: CASE 3: ZOOM OUT, GET PIECE, RESAMPLE UP, DRAW");
364
                                // get raster piece, paint on it, resample up:
365
                                
366
                                logger.debug("Piece of interest size: " + bi_w + ", " + bi_h);
367
                                BufferedImage bi = Utils.createBufferedImage(bi_w, bi_h);
368
                                
369
                                int bim_offset_x =
370
                                        Math.round( (float) (((interse.getMinX() - extent.getMinX()) / extent.getWidth()) * imgWidth));
371
                                int bim_offset_y =
372
                                        Math.round( (float) (((extent.getMaxY() - interse.getMaxY()) / extent.getHeight()) * imgHeight));
373
                                logger.debug("Piece of interest offset from raster: " + bim_offset_x + ", " + bim_offset_y);
374
                                bi.getGraphics().drawImage(wholeImage, -bim_offset_x, -bim_offset_y, null);
375
                                
376
                                // new bim size
377
                                bi_w =
378
                                        Math.round( (float) ((interse.getWidth() / vext.getWidth()) * viewPort.getImageWidth()));
379
                                bi_h =
380
                                        Math.round( (float) ((interse.getHeight() / vext.getHeight()) * viewPort.getImageHeight()));
381
                                
382
                                logger.debug("Piece of interest resize to: " + bi_w + ", " + bi_h);
383
                                Image smallImage = bi.getScaledInstance(bi_w, bi_h, Image.SCALE_FAST);
384
                                // new bim offset to screen
385
                                bim_offset_x =
386
                                        Math.round( (float) (((interse.getMinX() - vext.getMinX()) / vext.getWidth()) * viewPort.getImageWidth()));
387
                                bim_offset_y =
388
                                        Math.round( (float) (((vext.getMaxY() - interse.getMaxY()) / vext.getHeight()) * viewPort.getImageHeight()));
389
                                
390
                                logger.debug("Piece of interest offset from view: " + bim_offset_x + ", " + bim_offset_y);                                
391
                                g.drawImage(smallImage, bim_offset_x, bim_offset_y, null);
392
                                bi = null;
393
                        }
394
                }
395
                wholeImage = null;
396
                System.gc();
397
        }
398

    
399
        /**
400
         * @return the raster data type, that is, the data type of the individual bands.
401
         */
402
        public int getRasterDataType() {
403
                return DataBuffer.TYPE_BYTE;
404
        }
405

    
406
        /**
407
         * @return the data associated with a specified pixel and a particular band
408
         * 
409
         * @param x the x coordinate of the pixel
410
         * @param y the y coordinate of the pixel
411
         * @param band the 0-based band index
412
         */
413
        public Object getData(int x, int y, int band) {
414
                return null;
415
        }
416

    
417
        /**
418
         * Gets the RGB values of the pixel of interest
419
         * 
420
         * @param wcx x coordinate in map units
421
         * @param wcy y coordinate in map units
422
         * 
423
         * @return array of RGB values of the pixel of interest
424
         */
425
        public int[] getPixel(double wcx, double wcy) {
426

    
427
                int[] resp = new int[3];
428
                
429
                try {
430
                        Point2D.Double wpoint = new Point2D.Double(wcx, wcy);
431
                        Point2D.Double image_point = new Point2D.Double(0, 0);
432
                        at.inverseTransform(wpoint, image_point);
433
                        
434
                        int scrx = (int) Math.round(image_point.getX());
435
                        int scry = (int) Math.round(image_point.getY());
436
                        
437
                        Image wholeImage = getImageFromBytes(imageFileBytes);
438
                        BufferedImage bi = Utils.createBufferedImage(5, 5);
439
                        bi.getGraphics().drawImage(wholeImage, -scrx, -scry, null);
440
                        int pixel = bi.getRGB(0, 0);
441
                        
442
                        
443
                        resp[0] = (pixel >> 16) & 0xff;
444
                        resp[1] = (pixel >> 8) & 0xff;
445
                        resp[2] = pixel & 0xff;
446
                        
447
                } catch (Throwable e) {
448
                        logger.error("While getting pixel bands: " + e.getMessage());
449
                        resp[0] = -1;
450
                        resp[1] = -1;
451
                        resp[2] = -1;
452
                }
453
                return resp;
454
        }
455

    
456
        /**
457
         * @return the driver name
458
         */
459
        public String getName() {
460
                return "WKR raster driver";
461
        }
462

    
463
        /**
464
         * return raster coords + view pixels coords
465
         */
466
        private int[] getRasterTansformation(Rectangle2D viewRect, Rectangle2D imageExt,
467
                        int vw, int imgw, int imgh) {
468
                
469
                if (Utils.rectanglesItersect(viewRect, imageExt)) {
470
                        
471
                        double units_per_pixel_in_view = viewRect.getWidth() / (1.0 * vw);
472
                        double units_per_pixel_in_raster = imageExt.getWidth() / (1.0 * imgw);
473

    
474
                        if (units_per_pixel_in_raster > units_per_pixel_in_view) {
475
                                // zoom in
476
                                Rectangle2D interse = new Rectangle2D.Double();
477
                                Rectangle2D.intersect(viewRect, imageExt, interse);
478
                                
479
                                double interse_map_x_offset_to_img = interse.getMinX() - imageExt.getMinX();
480
                                double interse_map_y_offset_to_img = imageExt.getMaxY() - interse.getMaxY();
481
                                int interse_offset_in_img_x =
482
                                        Math.round((float) (interse_map_x_offset_to_img / units_per_pixel_in_raster));
483
                                int interse_offset_in_img_y =
484
                                        Math.round((float) (interse_map_y_offset_to_img / units_per_pixel_in_raster));
485
                                int inserse_pix_w = 
486
                                        Math.round((float) (interse.getWidth() / units_per_pixel_in_raster));
487
                                int inserse_pix_h = 
488
                                        Math.round((float) (interse.getHeight() / units_per_pixel_in_raster));
489
                                
490
                                
491
                                double interse_map_x_offset_to_view = interse.getMinX() - viewRect.getMinX();
492
                                double interse_map_y_offset_to_view = viewRect.getMaxY() - interse.getMaxY();
493
                                int interse_offset_in_view_x =
494
                                        Math.round((float) (interse_map_x_offset_to_view / units_per_pixel_in_view));
495
                                int interse_offset_in_view_y =
496
                                        Math.round((float) (interse_map_y_offset_to_view / units_per_pixel_in_view));
497
                                
498
                                int interse_final_w =
499
                                        Math.round((float) (interse.getWidth() / units_per_pixel_in_view));
500
                                int interse_final_h =
501
                                        Math.round((float) (interse.getHeight() / units_per_pixel_in_view));
502

    
503
                                int[] resp = new int[8];
504
                                resp[0] = interse_offset_in_img_x;
505
                                resp[1] = interse_offset_in_img_y;
506
                                
507
                                if (inserse_pix_w == 0) inserse_pix_w = 1;
508
                                resp[2] = inserse_pix_w;
509
                                if (inserse_pix_h == 0) inserse_pix_h = 1;
510
                                resp[3] = inserse_pix_h;
511
                                if (interse_final_w == 0) interse_final_w = 1;
512
                                resp[4] = interse_final_w;
513
                                if (interse_final_h == 0) interse_final_h = 1;
514
                                resp[5] = interse_final_h;
515
                                
516
                                resp[6] = interse_offset_in_view_x;
517
                                resp[7] = interse_offset_in_view_y;
518
                                return resp;
519

    
520
                        } else {
521
                                // downsample
522
                                int[] resp = new int[4];
523
                                double raster_map_x_offset_to_view = imageExt.getMinX() - viewRect.getMinX();
524
                                double raster_map_y_offset_to_view = viewRect.getMaxY() - imageExt.getMaxY();
525
                                
526
                                double raster_pix_x_offset_to_view =
527
                                        raster_map_x_offset_to_view / units_per_pixel_in_view;
528
                                double raster_pix_y_offset_to_view =
529
                                        raster_map_y_offset_to_view / units_per_pixel_in_view;
530
                                
531
                                resp[0] = Math.round((float) raster_pix_x_offset_to_view);
532
                                resp[1] = Math.round((float) raster_pix_y_offset_to_view);
533
                                resp[2] = Math.round(
534
                                                (float) ((1.0 * imgw) * units_per_pixel_in_raster / units_per_pixel_in_view)
535
                                                );
536
                                if (resp[2] == 0) resp[2] = 1;
537
                                resp[3] = Math.round(
538
                                                (float) ((1.0 * imgh) * units_per_pixel_in_raster / units_per_pixel_in_view)
539
                                                );
540
                                if (resp[3] == 0) resp[3] = 1;
541
                                return resp;
542
                        }
543

    
544
                } else {
545
                        return null;
546
                }
547
        }
548
        
549
        private static String[] getWorldFileExtensions(String ext) {
550

    
551
                if ((ext.compareToIgnoreCase("jpg") == 0) || (ext.compareToIgnoreCase("jpeg") == 0)) {
552
                        String[] resp = {"wld", "jgw", "jpw"};
553
                        return resp;
554
                }
555
                
556
                if ((ext.compareToIgnoreCase("gif") == 0) || (ext.compareToIgnoreCase("png") == 0)) {
557
                        String[] resp = {"wld"};
558
                        return resp;
559
                } else {
560
                        return null;
561
                }
562
        }
563

    
564
        /**
565
         * @return the raster file full path
566
         */
567
        public String getFilePath() {
568
                return path;
569
        }
570

    
571
}
572

    
573

    
574

    
575