Statistics
| Revision:

svn-gvsig-desktop / trunk / libraries / libCq CMS for java.old / src / org / cresques / io / EcwFile.java @ 4578

History | View | Annotate | Download (41.4 KB)

1
/*
2
 * Cresques Mapping Suite. Graphic Library for constructing mapping applications.
3
 *
4
 * Copyright (C) 2004-5.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
19
 *
20
 * For more information, contact:
21
 *
22
 * cresques@gmail.com
23
 */
24
package org.cresques.io;
25

    
26
import com.ermapper.ecw.JNCSFile;
27
import com.ermapper.ecw.JNCSFileNotOpenException;
28
import com.ermapper.ecw.JNCSInvalidSetViewException;
29
import com.ermapper.ecw.JNCSProgressiveUpdate;
30

    
31
import com.ermapper.util.JNCSDatasetPoint;
32
import com.ermapper.util.JNCSWorldPoint;
33

    
34
import org.cresques.cts.ICoordTrans;
35
import org.cresques.cts.IProjection;
36

    
37
import org.cresques.px.Extent;
38

    
39
import java.awt.Dimension;
40
import java.awt.Image;
41
import java.awt.Point;
42
import java.awt.image.BufferedImage;
43
import java.awt.image.PixelGrabber;
44

    
45

    
46
/**
47
 * Soporte para los ficheros .ecw de ErMapper.
48
 * <br>
49
 * NOTA: El SDK que ermapper ha puesto a disposici?n del p?blico en java
50
 * es una versi?n 2.45, de 19/11/2001. Est? implementada usando JNI que
51
 * se apoya en tres librer?as din?micas (dll), y presenta deficiencias
52
 * muy graves a la hora de acceder a la informaci?n. Hasta el momento
53
 * hemos detectado 3 de ellas:<BR>
54
 *     1?.- No soporta ampliaciones superiores a 1:1. si se intenta acceder
55
 * a un ecw con un zoom mayor da una excepci?n del tipo
56
 * com.ermapper.ecw.JNCSInvalidSetViewException, que de no ser tenida encuenta
57
 * acaba tirando abajo la m?quina virtual de java.<BR>
58
 *     2?.- La longitud m?xima de l?nea que adminte el m?todo readLineRGBA es
59
 * de unos 2500 pixeles, lo que hace el uso para la impresi?n en formatos
60
 * superiorea a A4 a 300 ppp o m?s inviable.<BR>
61
 *     3?.- La actualizaci?n progresiva usando el interface JNCSProgressiveUpdate
62
 * con el JNCSFile hace que el equipo genere un error severo y se apague. Este
63
 * error imposibilita esta t?cnica de acceso a ECW.<BR>
64
 * <br>
65
 * Para saltarnos la limitaci?n del bug#1 pedimos la ventana correspondiente al zoom 1:1 para
66
 * el view que nos han puesto, y la resizeamos al tama?o que nos pide el usuario.<br>
67
 * Como consecuencia del bug#2, para tama?os de ventana muy grandes (los necesarios
68
 * para imprimir a m?s de A4 a 300DPI), hay que hacer varias llamadas al fichero con
69
 * varios marcos contiguos, y los devolvemos 'pegados' en una sola imagen (esto se
70
 * realiza de manera transparente para el usuario dentro de la llamada a updateImage.<br>
71
 *
72
 * @author "Luis W. Sevilla" <sevilla_lui@gva.es>
73
 */
74
public class EcwFile extends GeoRasterFile implements JNCSProgressiveUpdate {
75
    //Lleva la cuenta del n?mero de actualizaciones que se hace de una imagen que corresponde con el 
76
    //n?mero de bandas que tiene. Esto es necesario ya que si una imagen tiene el mustResize a
77
    //true solo debe llamar a la funci?n resizeImage al actualizar la ?ltima banda, sino al hacer
78
    //un zoom menor que 1:1 se veria mal
79
    private static int nUpdate = 0;
80
    private JNCSFile file = null;
81
    private boolean bErrorOnOpen = false;
82
    private String errorMessage = null;
83
    private boolean multifile = false;
84
    private Extent v = null;
85

    
86
    // Ultimo porcentaje de refresco. Se carga en el update y se
87
    // actualiza en el refreshUpdate
88
    private int lastRefreshPercent = 0;
89

    
90
    public EcwFile(IProjection proj, String fName) {
91
        super(proj, null);
92
        fName = DataSource.normalize(fName);
93
        super.setName(fName);
94
        extent = new Extent();
95

    
96
        try {
97
            //System.err.println("Abriendo "+fName);
98
            file = new JNCSFile(fName, false);
99
            load();
100
            bandCount = file.numBands;
101

    
102
            if ( bandCount > 2) {
103
                    setBand(RED_BAND,   0);
104
                    setBand(GREEN_BAND, 1);
105
                    setBand(BLUE_BAND,  2);
106
            } else
107
                    setBand(RED_BAND|GREEN_BAND|BLUE_BAND, 0);
108
        } catch (Exception e) {
109
            bErrorOnOpen = true;
110
            errorMessage = e.getMessage();
111
            System.err.println(errorMessage);
112
            e.printStackTrace();
113
        }
114
    }
115

    
116
    /**
117
     * Carga un ECW.
118
     *
119
     * @param fname
120
     */
121
    public GeoFile load() {
122
        double minX;
123
        double minY;
124
        double maxX;
125
        double maxY;
126

    
127
        if(file.cellIncrementY > 0)
128
                file.cellIncrementY = -file.cellIncrementY;
129
        
130
        minX = file.originX;
131
        maxY = file.originY;
132
        maxX = file.originX +
133
               ((double) (file.width - 1) * file.cellIncrementX);
134
        minY = file.originY +
135
               ((double) (file.height - 1) * file.cellIncrementY);
136
        
137
        extent = new Extent(minX, minY, maxX, maxY);
138
      
139

    
140
        return this;
141
    }
142

    
143
    public void close() {
144
            if(file != null){
145
                    file.close(true);
146
                    file = null;
147
            }
148
    }
149

    
150
    /**
151
     * Devuelve el ancho de la imagen
152
     */
153
    public int getWidth() {
154
        return file.width;
155
    }
156

    
157
    /**
158
     * Devuelve el alto de la imagen
159
     */
160
    public int getHeight() {
161
        return file.height;
162
    }
163

    
164
    /**
165
     *
166
     */
167
    public void setMultifile(boolean mult) {
168
        this.multifile = mult;
169
    }
170

    
171
    public void setView(Extent e) {
172
        v = new Extent(e);
173
    }
174

    
175
    public Extent getView() {
176
        return v;
177
    }
178
    
179
    private void setFileView(int numBands, int [] bandList, ChunkFrame f)
180
            throws JNCSFileNotOpenException, JNCSInvalidSetViewException {
181

    
182
        file.setView(file.numBands, bandList, f.v.minX(),
183
                        f.v.maxY(), f.v.maxX(), f.v.minY(), f.width, f.height);
184
    }
185

    
186
    /**
187
     * Obtiene un trozo de imagen (determinado por la vista y los par?metros.
188
     *
189
     * @param width
190
     * @param height
191
     */
192
    public synchronized Image updateImage(int width, int height, ICoordTrans rp) {
193
        // TODO reproyectar para devolver el trozo de imagen pedida sobre ...
194
        // la proyecci?n de destino.
195
        int line = 0;
196
        boolean mustResize = false;
197
        Dimension fullSize = null;
198
        Image ecwImage = null;
199

    
200
        if (file == null) {
201
            return ecwImage;
202
        }
203

    
204
        try {
205
            double dFileAspect;
206
            double dWindowAspect;
207

    
208
            //double dWorldTLX, dWorldTLY, dWorldBRX, dWorldBRY;
209
            int[] bandlist;
210
            int[] bandListTriband;
211
            int[] pRGBArray = null;
212

    
213
            // Work out the correct aspect for the setView call.
214
            dFileAspect = (double) v.width() / (double) v.height();
215
            dWindowAspect = (double) width / (double) height;
216

    
217
            if (dFileAspect > dWindowAspect) {
218
                height = (int) ((double) width / dFileAspect);
219
            } else {
220
                width = (int) ((double) height * dFileAspect);
221
            }
222

    
223
            fullSize = new Dimension(width, height);
224

    
225
            //System.out.println("fullSize = ("+width+","+height+")");
226
            // Peta en los peque?os ... arreglar antes de meter del todo
227
            ChunkFrame[] frames = ChunkFrame.computeFrames(file, v, fullSize);
228

    
229
            /*for (int nChunk=0; nChunk<frames.length; nChunk++) {
230
                    System.out.println("chunck "+nChunk+"->"+frames[nChunk]);
231
            }*/
232
            if (frames.length == 1) {
233
                width = frames[0].width;
234
                height = frames[0].height;
235

    
236
                if (width <= 0) {
237
                    width = 1;
238
                }
239

    
240
                if (height <= 0) {
241
                    height = 1;
242
                }
243

    
244
                //System.out.println("frameSize = ("+width+","+height+")");
245
                //System.out.println("Cambio el ancho total a ("+width+","+height+")");
246
            }
247

    
248
            /*                        JNCSDatasetPoint ptMin = file.convertWorldToDataset(v.minX(), v.minY());
249
                                    JNCSDatasetPoint ptMax = file.convertWorldToDataset(v.maxX(), v.maxY());
250
                                    System.out.println("Dataset coords Width = "+(ptMax.x-ptMin.x)+", px width ="+width);
251
                                    // BEGIN Cambiando para soportar e < 1:1
252
                                    // TODO Mejorarlo para que los PAN con un zoom muy grande sean correctos
253
                                    if ((ptMax.x-ptMin.x)<width) {
254
                                            width = ptMax.x-ptMin.x;
255
                                            height = ptMin.y-ptMax.y;
256
                                            System.out.println("Size=("+width+","+height+")");
257
                                            mustResize = true;
258
                                    }*/
259

    
260
            // Create an image of the ecw file.
261
            if (doTransparency) {
262
                ecwImage = new BufferedImage(width, height,
263
                                             BufferedImage.TYPE_INT_ARGB);
264
            } else {
265
                ecwImage = new BufferedImage(width, height,
266
                                             BufferedImage.TYPE_INT_RGB);
267
            }
268

    
269
            pRGBArray = new int[width];
270

    
271
            // Setup the view parameters for the ecw file.
272
            bandlist = new int[bandCount];
273
            bandListTriband = new int[bandCount];
274

    
275
            if (bandCount > 2) {
276
                bandlist[0] = getBand(RED_BAND);
277
                bandlist[1] = getBand(GREEN_BAND);
278
                bandlist[2] = getBand(BLUE_BAND);
279

    
280
                if (bandCount > 3) {
281
                    for (int i = 3; i < bandCount; i++) {
282
                        bandlist[i] = 0;
283
                    }
284
                }
285
            } else {
286
                for (int i = 0; i < bandCount; i++) {
287
                    bandlist[i] = i;
288
                }
289
            }
290

    
291
            if (bandCount == 3) {
292
                bandListTriband[0] = 0;
293
                bandListTriband[1] = 1;
294
                bandListTriband[2] = 2;
295
            }
296

    
297
            for (int nChunk = 0; nChunk < frames.length; nChunk++) {
298
                ChunkFrame f = frames[nChunk];
299

    
300
                // Set the view                        
301
                if (bandCount != 3) {
302
                    setFileView(file.numBands, bandlist, f);
303
                } else {
304
                    setFileView(file.numBands, bandListTriband, f);
305
                }
306

    
307
                //System.out.println("f.Size = ("+f.width+","+f.height+")+CHUNK="+nChunk);
308

    
309
                /* Esta peli es porque el Ecw no intercambia las bandas con lo que me toca hacerlo
310
                 * a mano. Primero detectamos si se ha alterado el orden de las mismas. Si es as?
311
                 * calculamos mascaras y desplazamientos y hacemos una copia en pRGBArrayCopy
312
                 * con las bandas alteradas de orden
313
                 */
314
                int[] pRGBArrayCopy = null;
315
                int[] mascara = new int[3];
316
                int[] shl = new int[3];
317
                int[] shr = new int[3];
318
                boolean order = true;
319

    
320
                if (bandCount == 3) {
321
                    for (int i = 0; i < bandCount; i++)
322
                        if (bandlist[i] != i) {
323
                            order = false;
324
                        }
325

    
326
                    if (!order) {
327
                        for (int i = 0; i < bandCount; i++) {
328
                            switch (bandlist[i]) {
329
                            case 0:
330
                                mascara[i] = 0x00ff0000;
331

    
332
                                break;
333

    
334
                            case 1:
335
                                mascara[i] = 0x0000ff00;
336

    
337
                                break;
338

    
339
                            case 2:
340
                                mascara[i] = 0x000000ff;
341

    
342
                                break;
343
                            }
344

    
345
                            if ((i == 1) && (bandlist[i] == 0)) {
346
                                shr[i] = 8;
347
                            }
348

    
349
                            if ((i == 2) && (bandlist[i] == 0)) {
350
                                shr[i] = 16;
351
                            }
352

    
353
                            if ((i == 0) && (bandlist[i] == 1)) {
354
                                shl[i] = 8;
355
                            }
356

    
357
                            if ((i == 2) && (bandlist[i] == 1)) {
358
                                shr[i] = 8;
359
                            }
360

    
361
                            if ((i == 0) && (bandlist[i] == 2)) {
362
                                shl[i] = 16;
363
                            }
364

    
365
                            if ((i == 1) && (bandlist[i] == 2)) {
366
                                shl[i] = 8;
367
                            }
368
                        }
369
                    }
370
                }
371

    
372
                // Read the scan lines
373
                for (line = 0; line < f.height; line++) {
374
                    file.readLineRGBA(pRGBArray);
375

    
376
                    if ((bandCount == 3) && !order) {
377
                        pRGBArrayCopy = new int[pRGBArray.length];
378

    
379
                        for (int i = 0; i < pRGBArray.length; i++) {
380
                            pRGBArrayCopy[i] = (pRGBArray[i] & 0xff000000) +
381
                                               (((pRGBArray[i] & mascara[2]) << shl[2]) >> shr[2]) +
382
                                               (((pRGBArray[i] & mascara[1]) << shl[1]) >> shr[1]) +
383
                                               (((pRGBArray[i] & mascara[0]) << shl[0]) >> shr[0]);
384
                        }
385

    
386
                        pRGBArray = pRGBArrayCopy;
387
                    }
388

    
389
                    // Prueba de sustituci?n de color transparente
390
                    if (doTransparency) {
391
                        if (line == 0) {
392
                            tFilter.debug = true;
393
                        }
394

    
395
                        tFilter.filterLine(pRGBArray);
396
                        tFilter.debug = false;
397
                    }
398

    
399
                    //System.out.println("setRGB("+f.pos.x+","+f.pos.y+line+","+f.width+","+1+","+pRGBArray+","+0+","+f.width+")");
400
                    ((BufferedImage) ecwImage).setRGB(f.pos.x, f.pos.y + line,
401
                                                      f.width, 1, pRGBArray, 0,
402
                                                      f.width);
403
                }
404
            }
405

    
406
            if (frames[0].mustResize) {
407
                //System.out.println("resize "+fullSize);
408
                return resizeImage(fullSize, ecwImage);
409
            }
410

    
411
            /*
412
             * La excepci?n atrapada es la de 'zoom > 1:1 no valido'
413
            } catch (com.ermapper.ecw.JNCSInvalidSetViewException e) {
414
                    System.err.println(errorMessage);
415
                    e.printStackTrace(); */
416
        } catch (com.ermapper.ecw.JNCSException e) { //java.lang.ArrayIndexOutOfBoundsException:
417
            bErrorOnOpen = true;
418
            System.err.println("EcwFile JNCS Error en la l?nea " + line + "/" +
419
                               height);
420
            System.err.println(e.getMessage());
421
            e.printStackTrace();
422
        } catch (java.lang.ArrayIndexOutOfBoundsException e) { //:
423
            bErrorOnOpen = true;
424
            System.err.println("EcwFile ArrayIndex Error en la l?nea " + line +
425
                               "/" + height);
426
            System.err.println(e.getMessage());
427
            e.printStackTrace();
428
        } catch (Exception e) {
429
            bErrorOnOpen = true;
430
            errorMessage = e.getMessage();
431

    
432
            //                        g.drawString(errorMessage, 0, 50);
433
            System.err.println(errorMessage);
434
            e.printStackTrace();
435
        }
436

    
437
        lastRefreshPercent = file.getPercentComplete();
438
        System.out.println("Leido al " + lastRefreshPercent + " %.");
439

    
440
        return ecwImage;
441
    }
442

    
443
    /**
444
     * Redimensionado de imagen
445
     * La funci?n getScaledInstance nos devuelve un tipo image que no sirve por lo que
446
     * habr? que crear  buffImg como BufferedImage y copiar los datos devueltos por esta
447
     * funci?n a este que es el que ser? devuelto por la funci?n
448
     * @param sz
449
     * @param image        Image de entrada
450
     * @return        Imagen reescalada
451
     */
452
    private Image resizeImage(Dimension sz, Image image) {
453
        Image buffImg = null;
454
        Image img = image.getScaledInstance((int) sz.getWidth(),
455
                                            (int) sz.getHeight(),
456
                                            Image.SCALE_SMOOTH);
457

    
458
        //Todo este pollo es para copiar el tipo image devuelto a BufferedImage
459
        buffImg = new BufferedImage(img.getWidth(null), img.getHeight(null),
460
                                    BufferedImage.TYPE_INT_ARGB);
461

    
462
        int[] pixels = new int[img.getWidth(null) * img.getHeight(null)];
463
        PixelGrabber pg = new PixelGrabber(img, 0, 0, img.getWidth(null),
464
                                           img.getHeight(null), pixels, 0,
465
                                           img.getWidth(null));
466

    
467
        try {
468
            pg.grabPixels();
469
        } catch (InterruptedException e) {
470
            e.printStackTrace();
471
        }
472

    
473
        for (int j = 0; j < buffImg.getHeight(null); j++) {
474
            for (int i = 0; i < buffImg.getWidth(null); i++) {
475
                ((BufferedImage) buffImg).setRGB(i, j,
476
                                                 pixels[(j * buffImg.getWidth(null)) +
477
                                                 i]);
478
            }
479
        }
480

    
481
        return buffImg;
482
    }
483

    
484
    /**
485
     * Reproyecta el raster.
486
     */
487
    public void reProject(ICoordTrans rp) {
488
        // TODO metodo reProject pendiente de implementar
489
    }
490

    
491
    /**
492
     * Soporte para actualizaci?n de la imagen
493
     * @see com.ermapper.ecw.JNCSProgressiveUpdate#refreshUpdate(int, int, double, double, double, double)
494
     */
495
    public void refreshUpdate(int nWidth, int nHeight, double dWorldTLX,
496
                              double dWorldTLY, double dWorldBRX,
497
                              double dWorldBRY) {
498
        int completado = file.getPercentComplete();
499
        System.out.println("EcwFile: se actualiza 1: " + completado +
500
                           " % completado");
501

    
502
        if ((updatable != null) && (lastRefreshPercent < 100)) {
503
            if (((completado - lastRefreshPercent) > 25) ||
504
                    (completado == 100)) {
505
                lastRefreshPercent = file.getPercentComplete();
506
                updatable.repaint();
507
            }
508
        }
509
    }
510

    
511
    public void refreshUpdate(int nWidth, int nHeight, int dDatasetTLX,
512
                              int dDatasetTLY, int dDatasetBRX, int dDatasetBRY) {
513
        System.out.println("EcwFile: se actualiza 2");
514
    }
515

    
516
    /**
517
     *  Esta funci?n es porque el Ecw no intercambia las bandas con lo que me toca hacerlo
518
     * a mano. Primero detectamos si se ha alterado el orden de las mismas. Si es as?
519
     * calculamos mascaras y desplazamientos y hacemos una copia en pRGBArrayCopy
520
     * con las bandas alteradas de orden
521
     * @param bandList        lista de bandas
522
     * @param mask mascara
523
     * @param shl desplazamiento izquierda
524
     * @param shr desplazamiento derecha
525
     */
526
    private boolean calcMaskAndShift(int[] bandList, int[] mask, int[] shl,
527
                                     int[] shr) {
528
        boolean order = true;
529

    
530
        if (bandCount == 3) {
531
            for (int i = 0; i < bandCount; i++)
532
                if (bandList[i] != i) {
533
                    order = false;
534
                }
535

    
536
            if (!order) {
537
                for (int i = 0; i < bandCount; i++) {
538
                    switch (bandList[i]) {
539
                    case 0:
540
                        mask[i] = 0x00ff0000;
541

    
542
                        break;
543

    
544
                    case 1:
545
                        mask[i] = 0x0000ff00;
546

    
547
                        break;
548

    
549
                    case 2:
550
                        mask[i] = 0x000000ff;
551

    
552
                        break;
553
                    }
554

    
555
                    if ((i == 1) && (bandList[i] == 0)) {
556
                        shr[i] = 8;
557
                    }
558

    
559
                    if ((i == 2) && (bandList[i] == 0)) {
560
                        shr[i] = 16;
561
                    }
562

    
563
                    if ((i == 0) && (bandList[i] == 1)) {
564
                        shl[i] = 8;
565
                    }
566

    
567
                    if ((i == 2) && (bandList[i] == 1)) {
568
                        shr[i] = 8;
569
                    }
570

    
571
                    if ((i == 0) && (bandList[i] == 2)) {
572
                        shl[i] = 16;
573
                    }
574

    
575
                    if ((i == 1) && (bandList[i] == 2)) {
576
                        shl[i] = 8;
577
                    }
578
                }
579
            }
580
        }
581

    
582
        return order;
583
    }
584

    
585
    /**
586
     * Intercambio de bandas para ecw manual. Se le pasa el array de bytes que se desea intercambiar
587
     * la mascara y desplazamientos previamente calculados con calcMaskAndShift
588
     * @param order        true si el orden de las bandas no est? alterado y false si lo est?
589
     * @param pRGBArray array de
590
     * @param mascara
591
     * @param shl desplazamiento a izquierda
592
     * @param shr desplazamiento a derecha
593
     * @return array con las bandas cambiadas
594
     */
595
    private int[] changeBands(boolean order, int[] pRGBArray, int[] mascara,
596
                              int[] shl, int[] shr) {
597
        if ((bandCount == 3) && !order) {
598
            int[] pRGBArrayCopy = new int[pRGBArray.length];
599

    
600
            for (int i = 0; i < pRGBArray.length; i++) {
601
                pRGBArrayCopy[i] = (pRGBArray[i] & 0xff000000) +
602
                                   (((pRGBArray[i] & mascara[2]) << shl[2]) >> shr[2]) +
603
                                   (((pRGBArray[i] & mascara[1]) << shl[1]) >> shr[1]) +
604
                                   (((pRGBArray[i] & mascara[0]) << shl[0]) >> shr[0]);
605
            }
606

    
607
            return pRGBArrayCopy;
608
        }
609

    
610
        return pRGBArray;
611
    }
612

    
613
    /**
614
     * Asigna al objeto Image los valores con los dato de la imagen contenidos en el
615
     * vector de enteros.
616
     * @param image        imagen con los datos actuales
617
     * @param startX        inicio de la posici?n en X dentro de la imagen
618
     * @param startY        inicio de la posici?n en X dentro de la imagen
619
     * @param w        Ancho de la imagen
620
     * @param h        Alto de la imagen
621
     * @param rgbArray        vector que contiene la banda que se va a sustituir
622
     * @param offset        desplazamiento
623
     * @param scansize        tama?o de imagen recorrida por cada p
624
     */
625
    protected void setRGBLine(BufferedImage image, int startX, int startY,
626
                              int w, int h, int[] rgbArray, int offset,
627
                              int scansize) {
628
        image.setRGB(startX, startY, w, h, rgbArray, offset, scansize);
629
    }
630

    
631
    /**
632
     * Asigna al objeto Image la mezcla entre los valores que ya tiene y los valores
633
     * con los dato de la imagen contenidos en el vector de enteros. De los valores RGB
634
     * que ya contiene se mantienen las bandas que no coinciden con el valor de flags. La
635
     * banda correspondiente a flags es sustituida por los datos del vector.
636
     * @param image        imagen con los datos actuales
637
     * @param startX        inicio de la posici?n en X dentro de la imagen
638
     * @param startY        inicio de la posici?n en X dentro de la imagen
639
     * @param w        Ancho de la imagen
640
     * @param h        Alto de la imagen
641
     * @param rgbArray        vector que contiene la banda que se va a sustituir
642
     * @param offset        desplazamiento
643
     * @param scansize        tama?o de imagen recorrida por cada paso
644
     * @param flags        banda que se va a sustituir (Ctes de GeoRasterFile)
645
     */
646
    protected void setRGBLine(BufferedImage image, int startX, int startY,
647
                              int w, int h, int[] rgbArray, int offset,
648
                              int scansize, int flags) {
649
        int[] line = new int[rgbArray.length];
650
        image.getRGB(startX, startY, w, h, line, offset, scansize);
651

    
652
        if (flags == GeoRasterFile.RED_BAND) {
653
            for (int i = 0; i < line.length; i++)
654
                line[i] = (line[i] & 0x0000ffff) | (rgbArray[i] & 0xffff0000);
655
        } else if (flags == GeoRasterFile.GREEN_BAND) {
656
            for (int i = 0; i < line.length; i++)
657
                line[i] = (line[i] & 0x00ff00ff) | (rgbArray[i] & 0xff00ff00);
658
        } else if (flags == GeoRasterFile.BLUE_BAND) {
659
            for (int i = 0; i < line.length; i++)
660
                line[i] = (line[i] & 0x00ffff00) | (rgbArray[i] & 0xff0000ff);
661
        }
662

    
663
        image.setRGB(startX, startY, w, h, line, offset, scansize);
664
    }
665

    
666
    /**
667
     * Asigna al objeto Image la mezcla entre los valores que ya tiene y los valores
668
     * con los dato de la imagen contenidos en el vector de enteros. De los valores RGB
669
     * que ya contiene se mantienen las bandas que no coinciden con el valor de flags. La
670
     * banda correspondiente a flags es sustituida por los datos del vector.
671
     * @param image        imagen con los datos actuales
672
     * @param startX        inicio de la posici?n en X dentro de la imagen
673
     * @param startY        inicio de la posici?n en X dentro de la imagen
674
     * @param w        Ancho de la imagen
675
     * @param h        Alto de la imagen
676
     * @param rgbArray        vector que contiene la banda que se va a sustituir
677
     * @param offset        desplazamiento
678
     * @param scansize        tama?o de imagen recorrida por cada paso
679
     * @param origBand        Banda origen del GeoRasterFile
680
     * @param destBandFlag        banda que se va a sustituir (Ctes de GeoRasterFile)
681
     */
682
    protected void setRGBLine(BufferedImage image, int startX, int startY,
683
                              int w, int h, int[] rgbArray, int offset,
684
                              int scansize, int origBand, int destBandFlag) {
685
        int[] line = new int[rgbArray.length];
686
        image.getRGB(startX, startY, w, h, line, offset, scansize);
687

    
688
        if ((origBand == 0) && (destBandFlag == GeoRasterFile.RED_BAND)) {
689
            for (int i = 0; i < line.length; i++)
690
                line[i] = (line[i] & 0xff00ffff) | (rgbArray[i] & 0x00ff0000);
691
        } else if ((origBand == 1) &&
692
                       (destBandFlag == GeoRasterFile.GREEN_BAND)) {
693
            for (int i = 0; i < line.length; i++)
694
                line[i] = (line[i] & 0xffff00ff) | (rgbArray[i] & 0x0000ff00);
695
        } else if ((origBand == 2) &&
696
                       (destBandFlag == GeoRasterFile.BLUE_BAND)) {
697
            for (int i = 0; i < line.length; i++)
698
                line[i] = (line[i] & 0xffffff00) | (rgbArray[i] & 0x000000ff);
699
        }
700
        else if ((origBand == 0) && (destBandFlag == GeoRasterFile.GREEN_BAND)) {
701
            for (int i = 0; i < line.length; i++)
702
                line[i] = (line[i] & 0xffff00ff) |
703
                          ((rgbArray[i] & 0x00ff0000) >> 8);
704
        } else if ((origBand == 0) &&
705
                       (destBandFlag == GeoRasterFile.BLUE_BAND)) {
706
            for (int i = 0; i < line.length; i++)
707
                line[i] = (line[i] & 0xffffff00) |
708
                          ((rgbArray[i] & 0x00ff0000) >> 16);
709
        }
710
        else if ((origBand == 1) && (destBandFlag == GeoRasterFile.RED_BAND)) {
711
            for (int i = 0; i < line.length; i++)
712
                line[i] = (line[i] & 0xff00ffff) |
713
                          ((rgbArray[i] & 0x0000ff00) << 8);
714
        } else if ((origBand == 1) &&
715
                       (destBandFlag == GeoRasterFile.BLUE_BAND)) {
716
            for (int i = 0; i < line.length; i++)
717
                line[i] = (line[i] & 0xffffff00) |
718
                          ((rgbArray[i] & 0x0000ff00) >> 8);
719
        }
720
        else if ((origBand == 2) && (destBandFlag == GeoRasterFile.RED_BAND)) {
721
            for (int i = 0; i < line.length; i++)
722
                line[i] = (line[i] & 0xff00ffff) |
723
                          ((rgbArray[i] & 0x000000ff) << 16);
724
        } else if ((origBand == 2) &&
725
                       (destBandFlag == GeoRasterFile.GREEN_BAND)) {
726
            for (int i = 0; i < line.length; i++)
727
                line[i] = (line[i] & 0xffff00ff) |
728
                          ((rgbArray[i] & 0x000000ff) << 8);
729
        }
730

    
731
        image.setRGB(startX, startY, w, h, line, offset, scansize);
732
    }
733

    
734
    /* (non-Javadoc)
735
     * @see org.cresques.io.GeoRasterFile#updateImage(int, int, org.cresques.cts.ICoordTrans, java.awt.Image, int origBand, int destBand)
736
     */
737
    public Image updateImage(int width, int height, ICoordTrans rp, Image img,
738
                             int origBand, int destBandFlag) {
739
        //TODO reproyectar para devolver el trozo de imagen pedida sobre ...
740
        // la proyecci?n de destino.
741
        int line = 0;
742
        boolean mustResize = false;
743
        Dimension fullSize = null;
744

    
745
        if (file == null) {
746
            return null;
747
        }
748

    
749
        try {
750
            double dFileAspect;
751
            double dWindowAspect;
752

    
753
            //double dWorldTLX, dWorldTLY, dWorldBRX, dWorldBRY;
754
            int[] bandlist;
755
            int[] bandListTriband;
756
            int[] pRGBArray = null;
757

    
758
            // Work out the correct aspect for the setView call.
759
            dFileAspect = (double) v.width() / (double) v.height();
760
            dWindowAspect = (double) width / (double) height;
761

    
762
            if (dFileAspect > dWindowAspect) {
763
                height = (int) ((double) width / dFileAspect);
764
            } else {
765
                width = (int) ((double) height * dFileAspect);
766
            }
767

    
768
            fullSize = new Dimension(width, height);
769

    
770
            //System.out.println("fullSize = ("+width+","+height+")");
771
            // Peta en los peque?os ... arreglar antes de meter del todo
772
            ChunkFrame[] frames = ChunkFrame.computeFrames(file, v, fullSize);
773

    
774
            /*for (int nChunk=0; nChunk<frames.length; nChunk++) {
775
                    System.out.println("chunck "+nChunk+"->"+frames[nChunk]);
776
            }*/
777
            if (frames.length == 1) {
778
                width = frames[0].width;
779
                height = frames[0].height;
780

    
781
                if (width <= 0) {
782
                    width = 1;
783
                }
784

    
785
                if (height <= 0) {
786
                    height = 1;
787
                }
788

    
789
                //System.out.println("frameSize = ("+width+","+height+")");
790
                //System.out.println("Cambio el ancho total a ("+width+","+height+")");
791
            }
792

    
793
            // Create an image of the ecw file.
794
            pRGBArray = new int[width];
795

    
796
            // Setup the view parameters for the ecw file.
797
            bandlist = new int[bandCount];
798
            bandListTriband = new int[bandCount];
799

    
800
            if (bandCount > 2) {
801
                bandlist[0] = getBand(RED_BAND);
802
                bandlist[1] = getBand(GREEN_BAND);
803
                bandlist[2] = getBand(BLUE_BAND);
804

    
805
                if (bandCount > 3) {
806
                    for (int i = 3; i < bandCount; i++) {
807
                        bandlist[i] = 0;
808
                    }
809
                }
810
            } else {
811
                for (int i = 0; i < bandCount; i++) {
812
                    bandlist[i] = i;
813
                }
814
            }
815

    
816
            if (bandCount == 3) {
817
                bandListTriband[0] = 0;
818
                bandListTriband[1] = 1;
819
                bandListTriband[2] = 2;
820
            }
821

    
822
            int[] mascara = new int[3];
823
            int[] shl = new int[3];
824
            int[] shr = new int[3];
825
            boolean order = true;
826

    
827
            if (img == null) { //Caso en el que se crea un Image
828
                EcwFile.nUpdate = 1;
829

    
830
                Image ecwImage = new BufferedImage(width, height,
831
                                                   BufferedImage.TYPE_INT_ARGB);
832

    
833
                for (int nChunk = 0; nChunk < frames.length; nChunk++) {
834
                    ChunkFrame f = frames[nChunk];
835

    
836
                    if (bandCount != 3) {
837
                        setFileView(file.numBands, bandlist, f);
838
                    } else {
839
                        setFileView(file.numBands, bandListTriband, f);
840
                    }
841

    
842
                    order = calcMaskAndShift(bandlist, mascara, shl, shr);
843

    
844
                    for (line = 0; line < f.height; line++) {
845
                        file.readLineRGBA(pRGBArray);
846
                        pRGBArray = changeBands(
847
                                order, pRGBArray, mascara, shl, shr);
848
                        setRGBLine((BufferedImage) ecwImage, f.pos.x,
849
                                f.pos.y + line, f.width, 1, pRGBArray, 0, f.width);
850
                    }
851
                } //Chuncks
852

    
853
                applyAlpha(ecwImage);
854

    
855
                if (frames[0].mustResize && !this.multifile) {
856
                    return resizeImage(fullSize, ecwImage);
857
                }
858

    
859
                lastRefreshPercent = file.getPercentComplete();
860

    
861
                //System.out.println("Leido al "+lastRefreshPercent+" %.");
862
                return ecwImage;
863
            } else { //Caso en el que se actualiza una banda del Image
864
                EcwFile.nUpdate++;
865

    
866
                for (int nChunk = 0; nChunk < frames.length; nChunk++) {
867
                    ChunkFrame f = frames[nChunk];
868

    
869
                    if (bandCount != 3) {
870
                        setFileView(file.numBands, bandlist, f);
871
                    } else {
872
                        setFileView(file.numBands, bandListTriband, f);
873
                    }
874

    
875
                    order = calcMaskAndShift(bandlist, mascara, shl, shr);
876

    
877
                    //System.out.println("ORIGEN="+origBand+" DESTINO="+destBandFlag);
878
                    for (line = 0; line < f.height; line++) {
879
                        file.readLineRGBA(pRGBArray);
880
                        pRGBArray = changeBands(order, pRGBArray, mascara, shl, shr);
881
                        setRGBLine((BufferedImage) img, f.pos.x, f.pos.y + line,
882
                                f.width, 1, pRGBArray, 0, f.width, origBand, destBandFlag);
883
                    }
884
                } //Chuncks
885

    
886
                applyAlpha(img);
887

    
888
                if (frames[0].mustResize && (nUpdate == 3) && this.multifile) {
889
                    return resizeImage(fullSize, img);
890
                }
891

    
892
                lastRefreshPercent = file.getPercentComplete();
893

    
894
                //System.out.println("Leido al "+lastRefreshPercent+" %.");
895
                return img;
896
            }
897
        } catch (com.ermapper.ecw.JNCSException e) { //java.lang.ArrayIndexOutOfBoundsException:
898
            bErrorOnOpen = true;
899
            System.err.println("EcwFile JNCS Error en la l?nea " + line + "/" +
900
                               height);
901
            System.err.println(e.getMessage());
902
            e.printStackTrace();
903
        } catch (java.lang.ArrayIndexOutOfBoundsException e) { //:
904
            bErrorOnOpen = true;
905
            System.err.println("EcwFile ArrayIndex Error en la l?nea " + line +
906
                               "/" + height);
907
            System.err.println(e.getMessage());
908
            e.printStackTrace();
909
        } catch (Exception e) {
910
            bErrorOnOpen = true;
911
            errorMessage = e.getMessage();
912
            System.err.println(errorMessage);
913
            e.printStackTrace();
914
        }
915

    
916
        return img;
917
    }
918

    
919
    private void applyAlpha(Image im) {
920
        BufferedImage img = (BufferedImage) im;
921
        int alpha = (getAlpha() & 0xff) << 24;
922
        int w = img.getWidth();
923
        int[] line = new int[w];
924

    
925
        for (int j = 0; j < img.getHeight(); j++) {
926
            img.getRGB(0, j, w, 1, line, 0, w);
927

    
928
            for (int i = 0; i < w; i++)
929
                line[i] = (alpha | (line[i] & 0x00ffffff));
930

    
931
            img.setRGB(0, j, w, 1, line, 0, w);
932
        }
933
    }
934

    
935
    /* (non-Javadoc)
936
     * @see org.cresques.io.GeoRasterFile#getData(int, int)
937
     */
938
    public Object getData(int x, int y, int band) {
939
        //file.readLineRGBA();
940
        return null;
941
    }
942

    
943
    /**
944
     * Devuelve los datos de una ventana solicitada
945
     * @param ulX        coordenada X superior izda.
946
     * @param ulY        coordenada Y superior derecha.
947
     * @param sizeX        tama?o en X de la ventana.
948
     * @param sizeY tama?o en Y de la ventana.
949
     * @param band        Banda solicitada.
950
     */
951
    public byte[] getWindow(int ulX, int ulY, int sizeX, int sizeY, int band) {
952
        //TODO Nacho: Implementar getWindow de EcwFile
953
        return null;
954
    }
955

    
956
    /**
957
     * Obtiene la zona (Norte / Sur)
958
     * @return true si la zona es norte y false si es sur
959
     */
960
    public boolean getZone() {
961
        //TODO Nacho: Implementar getZone de EcwFile
962
        return false;
963
    }
964

    
965
    /**
966
     *Devuelve el n?mero de zona UTM
967
     *@return N?mero de zona
968
     */
969
    public int getUTM() {
970
        //                TODO Nacho: Implementar getUTM de EcwFile
971
        return 0;
972
    }
973

    
974
    /**
975
     * Obtiene el sistema de coordenadas geograficas
976
     * @return Sistema de coordenadas geogr?ficas
977
     */
978
    public String getGeogCS() {
979
        //TODO Nacho: Implementar getGeogCS de EcwFile
980
        return new String("");
981
    }
982

    
983
    /**
984
     * Devuelve el tama?o de bloque
985
     * @return Tama?o de bloque
986
     */
987
    public int getBlockSize() {
988
        //TODO Nacho: Implementar getBlockSize de EcwFile        
989
        return 1;
990
    }
991

    
992
    /**
993
     * Trozo de imagen (Chunk) en que se divide la consulta a la librer?a,
994
     * para esquivar el bug#2.
995
     *
996
     * @author luisw
997
     */
998
    static class ChunkFrame {
999
        // Ancho m?ximo (~2500 px)
1000
        final static int MAX_WIDTH = 1536;
1001

    
1002
        // Alto m?ximo (no hay l?mite)
1003
        final static int MAX_HEIGHT = 1536;
1004
        Point pos;
1005
        Extent v;
1006
        int width;
1007
        int height;
1008
        boolean mustResize = false;
1009

    
1010
        public ChunkFrame(Extent vista, int w, int h) {
1011
            v = vista;
1012
            width = w;
1013
            height = h;
1014
        }
1015

    
1016
        /**
1017
         * Calcula el array de chunks (trozos).
1018
         * @param file        Fichero ecw que hay que trocear.
1019
         * @param v Extent total de la vista.
1020
         * @param sz        Tama?o total de la vista.
1021
         * @return array de ChunkFrames.
1022
         * @throws JNCSFileNotOpenException
1023
         */
1024
        public static ChunkFrame[] computeFrames(JNCSFile file, Extent v,
1025
                                                 Dimension sz)
1026
                                          throws JNCSFileNotOpenException {
1027
            ChunkFrame[] frames = null;
1028
            ChunkFrame f = null;
1029

    
1030
            // Calcula el n? de chunks (filas y columnas)
1031
            int numCol = (sz.width / MAX_WIDTH);
1032

    
1033
            // Calcula el n? de chunks (filas y columnas)
1034
            int numRow = (sz.height / MAX_HEIGHT);
1035

    
1036
            if ((sz.width - (numCol * MAX_WIDTH)) > 0) {
1037
                numCol++;
1038
            }
1039

    
1040
            if ((sz.height - (numRow * MAX_HEIGHT)) > 0) {
1041
                numRow++;
1042
            }
1043

    
1044
            frames = new ChunkFrame[numCol * numRow];
1045

    
1046
            // Coprobaci?n previa para resolver el bug#1
1047
            JNCSDatasetPoint ptMin = file.convertWorldToDataset(v.minX(),
1048
                                                                v.minY());
1049
            JNCSDatasetPoint ptMax = file.convertWorldToDataset(v.maxX(),
1050
                                                                v.maxY());
1051

    
1052
            /*System.out.println("Dataset coords Width = "+(ptMax.x-ptMin.x)+", px width = "+sz.width+
1053
                            " px heigh = "+sz.height);*/
1054

    
1055
            // BEGIN Cambiando para soportar e < 1:1
1056
            // TODO Mejorarlo para que los PAN con un zoom muy grande sean correctos
1057
            //System.out.println("MAXX MINX-"+ptMax.x+","+ptMin.x);
1058
            if ((ptMax.x - ptMin.x) < sz.width) {
1059
                numCol = numRow = 1;
1060
                frames = new ChunkFrame[numCol * numRow];
1061
                f = frames[0] = new ChunkFrame(v, ptMax.x - ptMin.x,
1062
                                               ptMin.y - ptMax.y);
1063

    
1064
                //System.out.println("Size=("+f.width+","+f.height+")");
1065
                f.pos = new Point(0, 0);
1066
                f.mustResize = true;
1067
                f.v = new Extent(v);
1068
            } else {
1069
                // Calcula cada chunk
1070
                double stepx = ((double) ptMax.x - ptMin.x) / sz.getWidth();
1071
                double stepy = ((double) ptMax.y - ptMin.y) / sz.getHeight();
1072
                int h = sz.height;
1073

    
1074
                for (int r = 0; r < numRow; r++) {
1075
                    int w = sz.width;
1076

    
1077
                    for (int c = 0; c < numCol; c++) {
1078
                        f = new ChunkFrame(null, -1, -1);
1079

    
1080
                        // Posici?n del chunk
1081
                        f.pos = new Point(c * MAX_WIDTH, r * MAX_HEIGHT);
1082

    
1083
                        // Tama?o del chunk
1084
                        f.width = Math.min(MAX_WIDTH, w);
1085
                        f.height = Math.min(MAX_HEIGHT, h);
1086

    
1087
                        // Extent del chunk
1088
                        int x1 = ptMin.x + (int) (f.pos.x * stepx);
1089
                        int x2 = x1 + (int) (f.width * stepx);
1090
                        int y1 = ptMax.y - (int) (f.pos.y * stepy); //ptMax.y;
1091
                        int y2 = y1 - (int) (f.height * stepy); //ptMin.y;
1092
                        JNCSWorldPoint pt1 = file.convertDatasetToWorld(x1, y1);
1093
                        JNCSWorldPoint pt2 = file.convertDatasetToWorld(x2, y2);
1094
                        f.v = new Extent(pt1.x, pt1.y, pt2.x, pt2.y); // Hay que calcularlo
1095

    
1096
                        /*System.out.println(" View DataSet = ("+x1+","+y1+","+x2+","+y2+")");
1097
                        System.out.println(" View Extent  = "+ v);
1098
                        System.out.println("Frame Extent  = "+ f.v);*/
1099
                        frames[(r * numCol) + c] = f;
1100
                        w -= MAX_WIDTH;
1101
                    }
1102

    
1103
                    h -= MAX_HEIGHT;
1104
                }
1105
            }
1106

    
1107
            //System.out.println("Hay "+numRow+" filas y "+numCol+" columnas.");
1108
            return frames;
1109
        }
1110
    }
1111
}