svn-gvsig-desktop / branches / CqCMSDvp / libraries / libCq CMS for java.old / src / org / cresques / io / EcwFile.java @ 1809
History | View | Annotate | Download (14.6 KB)
1 |
/*
|
---|---|
2 |
* EcwFile.java
|
3 |
*/
|
4 |
package org.cresques.io; |
5 |
|
6 |
import java.awt.Dimension; |
7 |
import java.awt.Image; |
8 |
import java.awt.Point; |
9 |
import java.awt.image.BufferedImage; |
10 |
|
11 |
import com.ermapper.ecw.JNCSFile; |
12 |
import com.ermapper.ecw.JNCSFileNotOpenException; |
13 |
import com.ermapper.ecw.JNCSProgressiveUpdate; |
14 |
import com.ermapper.util.JNCSDatasetPoint; |
15 |
import com.ermapper.util.JNCSWorldPoint; |
16 |
import com.ermapper.ecw.JNCSException; |
17 |
|
18 |
import es.gva.cit.jmrsid.LTIMetadataRecord; |
19 |
import es.gva.cit.jmrsid.MrSIDException; |
20 |
|
21 |
import org.cresques.cts.ICoordTrans; |
22 |
import org.cresques.cts.IProjection; |
23 |
import org.cresques.px.*; |
24 |
|
25 |
|
26 |
/**
|
27 |
* Soporte para los ficheros .ecw de ErMapper.
|
28 |
* <br>
|
29 |
* NOTA: El SDK que ermapper ha puesto a disposici?n del p?blico en java
|
30 |
* es una versi?n 2.45, de 19/11/2001. Est? implementada usando JNI que
|
31 |
* se apoya en tres librer?as din?micas (dll), y presenta deficiencias
|
32 |
* muy graves a la hora de acceder a la informaci?n. Hasta el momento
|
33 |
* hemos detectado 3 de ellas:<BR>
|
34 |
* 1?.- No soporta ampliaciones superiores a 1:1. si se intenta acceder
|
35 |
* a un ecw con un zoom mayor da una excepci?n del tipo
|
36 |
* com.ermapper.ecw.JNCSInvalidSetViewException, que de no ser tenida encuenta
|
37 |
* acaba tirando abajo la m?quina virtual de java.<BR>
|
38 |
* 2?.- La longitud m?xima de l?nea que adminte el m?todo readLineRGBA es
|
39 |
* de unos 2500 pixeles, lo que hace el uso para la impresi?n en formatos
|
40 |
* superiorea a A4 a 300 ppp o m?s inviable.<BR>
|
41 |
* 3?.- La actualizaci?n progresiva usando el interface JNCSProgressiveUpdate
|
42 |
* con el JNCSFile hace que el equipo genere un error severo y se apague. Este
|
43 |
* error imposibilita esta t?cnica de acceso a ECW.<BR>
|
44 |
* <br>
|
45 |
* Para saltarnos la limitaci?n del bug#1 pedimos la ventana correspondiente al zoom 1:1 para
|
46 |
* el view que nos han puesto, y la resizeamos al tama?o que nos pide el usuario.<br>
|
47 |
* Como consecuencia del bug#2, para tama?os de ventana muy grandes (los necesarios
|
48 |
* para imprimir a m?s de A4 a 300DPI), hay que hacer varias llamadas al fichero con
|
49 |
* varios marcos contiguos, y los devolvemos 'pegados' en una sola imagen (esto se
|
50 |
* realiza de manera transparente para el usuario dentro de la llamada a updateImage.<br>
|
51 |
*
|
52 |
* @author "Luis W. Sevilla" <sevilla_lui@gva.es>
|
53 |
*/
|
54 |
|
55 |
public class EcwFile extends GeoRasterFile implements JNCSProgressiveUpdate { |
56 |
private JNCSFile file = null; |
57 |
private boolean bErrorOnOpen = false; |
58 |
private String errorMessage = null; |
59 |
|
60 |
private Extent v = null; |
61 |
|
62 |
// Ultimo porcentaje de refresco. Se carga en el update y se
|
63 |
// actualiza en el refreshUpdate
|
64 |
private int lastRefreshPercent = 0; |
65 |
|
66 |
public EcwFile(IProjection proj, String fName) { |
67 |
super(proj, null); |
68 |
fName = DataSource.normalize(fName);
|
69 |
super.setName(fName);
|
70 |
extent = new Extent();
|
71 |
try {
|
72 |
System.err.println("Abriendo "+fName); |
73 |
file = new JNCSFile(fName, false); |
74 |
//file = new JNCSFile(fName, true);
|
75 |
//file.addProgressiveUpdateListener(this);
|
76 |
load(); |
77 |
bandCount = file.numBands; |
78 |
if ( bandCount > 2) { |
79 |
setBand(RED_BAND, 0);
|
80 |
setBand(GREEN_BAND, 1);
|
81 |
setBand(BLUE_BAND, 2);
|
82 |
} else
|
83 |
setBand(RED_BAND|GREEN_BAND|BLUE_BAND, 0);
|
84 |
} catch(Exception e) { |
85 |
bErrorOnOpen = true;
|
86 |
errorMessage = e.getMessage(); |
87 |
// g.drawString(errorMessage, 0, 50);
|
88 |
System.err.println(errorMessage);
|
89 |
e.printStackTrace(); |
90 |
} |
91 |
} |
92 |
|
93 |
public void setView(Extent e) { v = new Extent(e); } |
94 |
public Extent getView() { return v; } |
95 |
|
96 |
/**
|
97 |
* Carga un ECW.
|
98 |
*
|
99 |
* @param fname
|
100 |
*/
|
101 |
|
102 |
public GeoFile load() {
|
103 |
double minX, minY, maxX, maxY;
|
104 |
|
105 |
System.out.println("ECW size = ("+file.width+","+file.height+")\n"+ |
106 |
" inc=("+file.cellIncrementX+","+file.cellIncrementY+")\n"+ |
107 |
" datum='"+file.datum+"', proyeccion='"+file.projection+"'"); |
108 |
minX = file.originX; |
109 |
maxY = file.originY; |
110 |
maxX = file.originX + (double)(file.width-1)*file.cellIncrementX; |
111 |
minY = file.originY + (double)(file.height-1)*file.cellIncrementY; |
112 |
extent = new Extent(minX, minY, maxX, maxY);
|
113 |
System.out.println(extent);
|
114 |
|
115 |
return this; |
116 |
} |
117 |
|
118 |
public void close() { |
119 |
file.close(true);
|
120 |
file = null;
|
121 |
} |
122 |
|
123 |
public int getWidth() { return file.width; } |
124 |
public int getHeight() { return file.height; } |
125 |
|
126 |
/**
|
127 |
* Trozo de imagen (Chunk) en que se divide la consulta a la librer?a,
|
128 |
* para esquivar el bug#2.
|
129 |
*
|
130 |
* @author luisw
|
131 |
*/
|
132 |
static class ChunkFrame { |
133 |
// Ancho m?ximo (~2500 px)
|
134 |
final static int MAX_WIDTH = 1536; |
135 |
// Alto m?ximo (no hay l?mite)
|
136 |
final static int MAX_HEIGHT = 1536; |
137 |
Point pos;
|
138 |
Extent v; |
139 |
int width, height;
|
140 |
boolean mustResize = false; |
141 |
public ChunkFrame(Extent vista, int w, int h) { |
142 |
v = vista; |
143 |
width = w; |
144 |
height = h; |
145 |
} |
146 |
/**
|
147 |
* Calcula el array de chunks (trozos).
|
148 |
* @param file Fichero ecw que hay que trocear.
|
149 |
* @param v Extent total de la vista.
|
150 |
* @param sz Tama?o total de la vista.
|
151 |
* @return array de ChunkFrames.
|
152 |
* @throws JNCSFileNotOpenException
|
153 |
*/
|
154 |
public static ChunkFrame [] computeFrames(JNCSFile file, Extent v, Dimension sz) throws JNCSFileNotOpenException { |
155 |
ChunkFrame [] frames = null; |
156 |
ChunkFrame f = null;
|
157 |
|
158 |
// Calcula el n? de chunks (filas y columnas)
|
159 |
int numCol = (sz.width / MAX_WIDTH), numRow = (sz.height / MAX_HEIGHT);
|
160 |
if (sz.width -(numCol*MAX_WIDTH) > 0) numCol++; |
161 |
if (sz.height - (numRow*MAX_HEIGHT) > 0) numRow++; |
162 |
frames = new ChunkFrame[numCol*numRow];
|
163 |
|
164 |
// Coprobaci?n previa para resolver el bug#1
|
165 |
JNCSDatasetPoint ptMin = file.convertWorldToDataset(v.minX(), v.minY()); |
166 |
JNCSDatasetPoint ptMax = file.convertWorldToDataset(v.maxX(), v.maxY()); |
167 |
System.out.println("Dataset coords Width = "+(ptMax.x-ptMin.x)+", px width = "+sz.width+ |
168 |
" px heigh = "+sz.height);
|
169 |
// BEGIN Cambiando para soportar e < 1:1
|
170 |
// TODO Mejorarlo para que los PAN con un zoom muy grande sean correctos
|
171 |
System.out.println("MAXX MINX-"+ptMax.x+","+ptMin.x); |
172 |
if ((ptMax.x-ptMin.x)<sz.width) {
|
173 |
numCol = numRow = 1;
|
174 |
frames = new ChunkFrame[numCol*numRow];
|
175 |
f = frames[0] = new ChunkFrame(v, ptMax.x-ptMin.x, ptMin.y-ptMax.y); |
176 |
System.out.println("Size=("+f.width+","+f.height+")"); |
177 |
f.pos = new Point(0,0); |
178 |
f.mustResize = true;
|
179 |
f.v = new Extent(v);
|
180 |
} else {
|
181 |
// Calcula cada chunk
|
182 |
double stepx = ((double) ptMax.x-ptMin.x)/sz.getWidth(); |
183 |
double stepy = ((double) ptMax.y-ptMin.y)/sz.getHeight(); |
184 |
int h = sz.height;
|
185 |
for (int r=0; r<numRow; r++) { |
186 |
int w = sz.width;
|
187 |
for (int c = 0; c<numCol; c++) { |
188 |
f = new ChunkFrame(null, -1, -1); |
189 |
// Posici?n del chunk
|
190 |
f.pos = new Point(c*MAX_WIDTH, r*MAX_HEIGHT); |
191 |
// Tama?o del chunk
|
192 |
f.width = Math.min(MAX_WIDTH, w);
|
193 |
f.height = Math.min(MAX_HEIGHT, h);
|
194 |
// Extent del chunk
|
195 |
int x1 = ptMin.x+(int) (f.pos.x*stepx); |
196 |
int x2 = x1+(int)(f.width*stepx); |
197 |
int y1 = ptMax.y-(int) (f.pos.y*stepy); //ptMax.y; |
198 |
int y2 = y1-(int)(f.height*stepy);//ptMin.y; |
199 |
JNCSWorldPoint pt1 = file.convertDatasetToWorld(x1, y1); |
200 |
JNCSWorldPoint pt2 = file.convertDatasetToWorld(x2, y2); |
201 |
f.v = new Extent(pt1.x, pt1.y, pt2.x, pt2.y); // Hay que calcularlo |
202 |
System.out.println(" View DataSet = ("+x1+","+y1+","+x2+","+y2+")"); |
203 |
System.out.println(" View Extent = "+ v); |
204 |
System.out.println("Frame Extent = "+ f.v); |
205 |
|
206 |
frames[r*numCol+c] = f; |
207 |
w -= MAX_WIDTH; |
208 |
} |
209 |
h -= MAX_HEIGHT; |
210 |
} |
211 |
} |
212 |
System.out.println("Hay "+numRow+" filas y "+numCol+" columnas."); |
213 |
return frames;
|
214 |
} |
215 |
} |
216 |
|
217 |
/**
|
218 |
* Obtiene un trozo de imagen (determinado por la vista y los par?metros.
|
219 |
*
|
220 |
* @param width
|
221 |
* @param height
|
222 |
*/
|
223 |
|
224 |
public synchronized Image updateImage(int width, int height, ICoordTrans rp) { |
225 |
// TODO reproyectar para devolver el trozo de imagen pedida sobre ...
|
226 |
// la proyecci?n de destino.
|
227 |
int line = 0; |
228 |
boolean mustResize = false; |
229 |
Dimension fullSize = null; |
230 |
Image ecwImage = null; |
231 |
if (file == null) return ecwImage; |
232 |
try {
|
233 |
double dFileAspect, dWindowAspect;
|
234 |
//double dWorldTLX, dWorldTLY, dWorldBRX, dWorldBRY;
|
235 |
int bandlist[]; |
236 |
int pRGBArray[] = null; |
237 |
|
238 |
// Work out the correct aspect for the setView call.
|
239 |
dFileAspect = (double)v.width()/(double)v.height(); |
240 |
dWindowAspect = (double)width /(double)height; |
241 |
|
242 |
if (dFileAspect > dWindowAspect) {
|
243 |
height =(int)((double)width/dFileAspect); |
244 |
} else {
|
245 |
width = (int)((double)height*dFileAspect); |
246 |
} |
247 |
|
248 |
|
249 |
fullSize = new Dimension(width, height); |
250 |
System.out.println("fullSize = ("+width+","+height+")"); |
251 |
// Peta en los peque?os ... arreglar antes de meter del todo
|
252 |
ChunkFrame frames[] = ChunkFrame.computeFrames(file, v, fullSize);
|
253 |
for (int nChunk=0; nChunk<frames.length; nChunk++) { |
254 |
System.out.println("chunck "+nChunk+"->"+frames[nChunk]); |
255 |
} |
256 |
if (frames.length == 1) { |
257 |
width = frames[0].width;
|
258 |
height = frames[0].height;
|
259 |
if(width<=0)width=1; |
260 |
if(height<=0)height=1; |
261 |
System.out.println("frameSize = ("+width+","+height+")"); |
262 |
//System.out.println("Cambio el ancho total a ("+width+","+height+")");
|
263 |
} |
264 |
|
265 |
/* JNCSDatasetPoint ptMin = file.convertWorldToDataset(v.minX(), v.minY());
|
266 |
JNCSDatasetPoint ptMax = file.convertWorldToDataset(v.maxX(), v.maxY());
|
267 |
System.out.println("Dataset coords Width = "+(ptMax.x-ptMin.x)+", px width ="+width);
|
268 |
// BEGIN Cambiando para soportar e < 1:1
|
269 |
// TODO Mejorarlo para que los PAN con un zoom muy grande sean correctos
|
270 |
if ((ptMax.x-ptMin.x)<width) {
|
271 |
width = ptMax.x-ptMin.x;
|
272 |
height = ptMin.y-ptMax.y;
|
273 |
System.out.println("Size=("+width+","+height+")");
|
274 |
mustResize = true;
|
275 |
}*/
|
276 |
// Create an image of the ecw file.
|
277 |
if (doTransparency)
|
278 |
ecwImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); |
279 |
else
|
280 |
ecwImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); |
281 |
|
282 |
pRGBArray = new int[width]; |
283 |
|
284 |
// Setup the view parameters for the ecw file.
|
285 |
|
286 |
if ( bandCount > 2) { |
287 |
bandlist = new int[bandCount]; |
288 |
bandlist[0] = getBand(RED_BAND);
|
289 |
bandlist[1] = getBand(GREEN_BAND);
|
290 |
bandlist[2] = getBand(BLUE_BAND);
|
291 |
if (bandCount > 3) |
292 |
for (int i=3; i< bandCount; i++) { |
293 |
bandlist[i] = 0;
|
294 |
} |
295 |
} else {
|
296 |
bandlist = new int[bandCount]; |
297 |
for (int i=0; i< bandCount; i++) { |
298 |
bandlist[i] = i; |
299 |
} |
300 |
} |
301 |
|
302 |
for (int nChunk=0; nChunk<frames.length; nChunk++) { |
303 |
ChunkFrame f = frames[nChunk]; |
304 |
// Set the view
|
305 |
file.setView(file.numBands, bandlist, f.v.minX(), f.v.maxY(), f.v.maxX(), f.v.minY(), f.width, f.height); |
306 |
System.out.println("f.Size = ("+f.width+","+f.height+")+CHUNK="+nChunk); |
307 |
|
308 |
|
309 |
// Read the scan lines
|
310 |
for (line=0; line < f.height; line++) { |
311 |
file.readLineRGBA(pRGBArray); |
312 |
// Prueba de sustituci?n de color transparente
|
313 |
if (doTransparency) {
|
314 |
if (line == 0) tFilter.debug = true; |
315 |
tFilter.filterLine(pRGBArray); |
316 |
tFilter.debug = false;
|
317 |
} |
318 |
//System.out.println("setRGB("+f.pos.x+","+f.pos.y+line+","+f.width+","+1+","+pRGBArray+","+0+","+f.width+")");
|
319 |
((BufferedImage)ecwImage).setRGB(f.pos.x, f.pos.y+line, f.width, 1, pRGBArray, 0, f.width); |
320 |
} |
321 |
} |
322 |
if (frames[0].mustResize) { |
323 |
//System.out.println("resize "+fullSize);
|
324 |
return resizeImage(fullSize, ecwImage);
|
325 |
} |
326 |
/*
|
327 |
* La excepci?n atrapada es la de 'zoom > 1:1 no valido'
|
328 |
} catch (com.ermapper.ecw.JNCSInvalidSetViewException e) {
|
329 |
System.err.println(errorMessage);
|
330 |
e.printStackTrace(); */
|
331 |
} catch(com.ermapper.ecw.JNCSException e) { //java.lang.ArrayIndexOutOfBoundsException: |
332 |
bErrorOnOpen = true;
|
333 |
System.err.println("EcwFile JNCS Error en la l?nea "+line+"/"+height); |
334 |
System.err.println(e.getMessage());
|
335 |
e.printStackTrace(); |
336 |
} catch(java.lang.ArrayIndexOutOfBoundsException e) { //: |
337 |
bErrorOnOpen = true;
|
338 |
System.err.println("EcwFile ArrayIndex Error en la l?nea "+line+"/"+height); |
339 |
System.err.println(e.getMessage());
|
340 |
e.printStackTrace(); |
341 |
} catch(Exception e) { |
342 |
bErrorOnOpen = true;
|
343 |
errorMessage = e.getMessage(); |
344 |
// g.drawString(errorMessage, 0, 50);
|
345 |
System.err.println(errorMessage);
|
346 |
e.printStackTrace(); |
347 |
} |
348 |
lastRefreshPercent = file.getPercentComplete(); |
349 |
System.out.println("Leido al "+lastRefreshPercent+" %."); |
350 |
return ecwImage;
|
351 |
} |
352 |
|
353 |
private Image resizeImage(Dimension sz, Image image) { |
354 |
return image.getScaledInstance((int) sz.getWidth(), |
355 |
(int) sz.getHeight(), Image.SCALE_SMOOTH); |
356 |
} |
357 |
|
358 |
/**
|
359 |
* Reproyecta el raster.
|
360 |
*/
|
361 |
public void reProject(ICoordTrans rp) { |
362 |
// TODO metodo reProject pendiente de implementar
|
363 |
} |
364 |
|
365 |
|
366 |
/**
|
367 |
* Soporte para actualizaci?n de la imagen
|
368 |
* @see com.ermapper.ecw.JNCSProgressiveUpdate#refreshUpdate(int, int, double, double, double, double)
|
369 |
*/
|
370 |
public void refreshUpdate(int nWidth, int nHeight, double dWorldTLX, double dWorldTLY, double dWorldBRX, double dWorldBRY) { |
371 |
int completado = file.getPercentComplete();
|
372 |
System.out.println("EcwFile: se actualiza 1: "+completado+" % completado"); |
373 |
if ((updatable != null) && (lastRefreshPercent < 100)) { |
374 |
if ((completado-lastRefreshPercent > 25) || completado == 100) { |
375 |
lastRefreshPercent = file.getPercentComplete(); |
376 |
updatable.repaint(); |
377 |
} |
378 |
} |
379 |
} |
380 |
|
381 |
public void refreshUpdate(int nWidth, int nHeight, int dDatasetTLX, int dDatasetTLY, int dDatasetBRX, int dDatasetBRY) { |
382 |
System.out.println("EcwFile: se actualiza 2"); |
383 |
} |
384 |
|
385 |
/* (non-Javadoc)
|
386 |
* @see org.cresques.io.GeoRasterFile#updateImage(int, int, org.cresques.cts.ICoordTrans, java.awt.Image, int)
|
387 |
*/
|
388 |
public Image updateImage(int width, int height, ICoordTrans rp, Image img, int flags) { |
389 |
// TODO Auto-generated method stub
|
390 |
return null; |
391 |
} |
392 |
|
393 |
/* (non-Javadoc)
|
394 |
* @see org.cresques.io.GeoRasterFile#getData(int, int)
|
395 |
*/
|
396 |
public Object getData(int x, int y, int band) { |
397 |
//file.readLineRGBA();
|
398 |
return null; |
399 |
} |
400 |
|
401 |
/**
|
402 |
* Devuelve los datos de una ventana solicitada
|
403 |
* @param ulX coordenada X superior izda.
|
404 |
* @param ulY coordenada Y superior derecha.
|
405 |
* @param sizeX tama?o en X de la ventana.
|
406 |
* @param sizeY tama?o en Y de la ventana.
|
407 |
* @param band Banda solicitada.
|
408 |
*/
|
409 |
|
410 |
public byte[] getWindow(int ulX, int ulY, int sizeX, int sizeY, int band){ |
411 |
//TODO Nacho: Implementar getWindow de EcwFile
|
412 |
return null; |
413 |
|
414 |
} |
415 |
|
416 |
/**
|
417 |
* Obtiene la zona (Norte / Sur)
|
418 |
* @return true si la zona es norte y false si es sur
|
419 |
*/
|
420 |
|
421 |
public boolean getZone(){ |
422 |
//TODO Nacho: Implementar getZone de EcwFile
|
423 |
return false; |
424 |
} |
425 |
|
426 |
/**
|
427 |
*Devuelve el n?mero de zona UTM
|
428 |
*@return N?mero de zona
|
429 |
*/
|
430 |
|
431 |
public int getUTM(){ |
432 |
// TODO Nacho: Implementar getUTM de EcwFile
|
433 |
return 0; |
434 |
} |
435 |
|
436 |
/**
|
437 |
* Obtiene el sistema de coordenadas geograficas
|
438 |
* @return Sistema de coordenadas geogr?ficas
|
439 |
*/
|
440 |
public String getGeogCS(){ |
441 |
//TODO Nacho: Implementar getGeogCS de EcwFile
|
442 |
return new String(""); |
443 |
} |
444 |
|
445 |
/**
|
446 |
* Devuelve el tama?o de bloque
|
447 |
* @return Tama?o de bloque
|
448 |
*/
|
449 |
public int getBlockSize(){ |
450 |
//TODO Nacho: Implementar getBlockSize de EcwFile
|
451 |
return 1; |
452 |
} |
453 |
} |