|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1.Introducción
La librería de Cresques consta de los siguientes elementos:
Un interfaz homogeneo de acceso a los datos de todos los formatos soportados .
Drivers de acceso de lectura a formatos raster georeferenciados como ecw, mrsid, tif, jpg, png a través de sus respectivas librerias.
Drivers de acceso a formatos Dxf y gml.
Drivers de escritura para GeoTiff y Ecw(solo linux kernel 2.4)
Una arquitectura para filtros sobre los formatos raster soportados.
Una interfaz gráfica en java para la gestión de filtros.
Una interfaz gráfica en java para el manejo del salvado a raster.
Los drivers de Cresques son clases que contiene un interfaz de acceso a un tipo de fichero a través de una librería o implementando sus propias funcionalidades. Para el acceso a Ecw, MrSID y Gdal utiliza librerias externas en C por lo que deberán estar instaladas correctamente para hacer uso de estos drivers. Las librerias externas al estar en C necestan un interfaz para el uso desde java. Este interfaz puede constar de otra librería en C que debe estar también instalada y un fichero .jar con las funciones en java de acceso a la librería que debe estar en el classpath. El directorio depend del proyecto de Cresques contiene las librerias C necesarias y el directorio lib las de java.
Los drivers de lectura y escritura para un mismo tipo de fichero están separadas en clases disintas.
El nombre de la clase de un driver de lectura está compuesto por el tipo de fichero al que accede seguido de la palabra File, así tenemos los siguientes drivers de lectura:
EcwFile para el driver de acceso a ficheros con formato ecw.
MrSIDFile para el driver de acceso a ficheros con formato MrSID.
GdalFile para el acceso a ficheros raster a través de la librería gdal.
DxfFile para acceso a ficheros dxf.
GmlFile para acceso a ficheros gml.
Los drivers de lectura de acceso a raster hereda todos de una misma clase abstracta GeoRasterFile para darles a todos ellos una interfaz homogenea de acceso a los datos. Los drivers que dependen de GeoRasterFile deben usar un mecanismo de registro para ser accesible. Este mecanismo permite que si creamos un driver fuera de cresques pero que herede de GeoRasterFile sea reconocido por este y pueda accederse a su funcionalidad. Para el registro, el driver en cuestión deberá incluir un bloque static en su código y añadir una entrada en la variable TreeMap supportedExtensions de GeoRasterFile con el nombre del driver y la clase que lo implementa. Esto se hará con la función registerExtension. Por ejemplo, si quisieramos hacer un driver para acceso a Jpg que use nuestra propia librería le incluiriamos un código como este:
static { registerExtension("jpg", JpgGeoRefFile.class); }
Sin necesidad de instanciar la nueva clase se ejecutará el bloque static asignando a la gestión de la extensión jpg la nueva clase creada. Como esta nuevo driver heredará de GeoRasterFile tendrá el interfaz necesario para acceso a los datos por lo que no habrá ningún problema. Por lo tanto, los métodos abstractos de GeoRasterFile son de obligatoria implementación en nuestro driver. Los drivers de GeoRasterFile están registrados en esta misma clase ya que son drivers que no varian dentro de Cresques.
El nombre de la clase de un driver de escritura está compuesto por el tipo de fichero al que accede seguido de la palabra Writer, así tenemos los siguientes drivers de lectura:
EcwWriter para el driver de escritura sobre ficheros con formato ecw (solo Linux kernel 2.4).
GdalWriter para la escritura sobre ficheros georeferenciados a través de Gdal.
Estos drivers son para la escritura de ficheros raster georeferenciados.
Los drivers de escritura en raster georeferenciados deben heredar de la misma clase abstracta GeoRasterWriter para darles a todos ellos una interfaz homogenea de escritura de datos. Los drivers que dependen de GeoRasterFile deben usar un mecanismo de registro para ser accesible. Este mecanismo permite que si creamos un driver fuera de cresques pero que herede de GeoRasterWriter sea reconocido por este y pueda accederse a su funcionalidad. Para el registro, el driver en cuestión deberá incluir un bloque static en su código y añadir una entrada en la variable TreeMap supportedExtensions de GeoRasterFile con el nombre del driver y la clase que lo implementa. Esto se hará con la función registerWriterExtension. Por ejemplo, si quisieramos hacer un driver de escritura en Jpg que use nuestra propia librería le incluiriamos un código como este:
static { registerWriterExtension("jpg", JpgGeoRefWriter.class); }
Dentro del driver tendremos que escribir una clase que deberá llamarse <NombreFormato>SupportOptions y que deberá heredar de WriterSupportOptions. Esta clase contendrá las opciones de escritura concretas para este formato. Esta opciones son las que el usuario visualizará en la ventana de propiedades de escritura. Es conveniente que las opciones que corresponderan a una lista de selección (combo) se guarden aquí como un vector de Strings o cualquier otro tipo de lista de esta forma:
private String[] formatList = {"NONE","UINT8","YUV","MULTI","RGB"};
y además se definan variables para los valores seleccionados por defecto
Deben crearse métodos para la lectura y escritura de todas las propiedades
El constructor estará vacio en funcionalidades pero llamará al constructor del padre pasando como parámetro un string que identifica al driver.
EcwSupportOptions(){ super("Ecw"); }
Constructor sin parámetros:
Este constructor inicializa el xxxSupportOptions que contiene las opciones de escritura y asigna valores a estas opciones especificas del driver que estamos implementando
public EcwWriter(){ this.support = new EcwSupportOptions(); this.driver = "ecw"; this.support.setBlockSize(64); this.support.setCompressionDefault(10); this.support.setFormatDefault(4); this.support.setWriteGeoref(true); this.ident = "Ecw"; this.consulta = true; }
Constructor para salvar una sola imagen completa
public EcwWriter( PxRaster raster, String outfilename, String infilename, int compresion)throws EcwException, IOException
Para este constructor se ha de pasar como parámetro el PxRaster de la imagen que se desea salvar a disco. Se inicializará la clase xxxSupportOptions de la misma forma que en el constructor anterior y se asignarán valores a variables de instancia con datos para salvar a raster tales como georeferenciación, número de bandas, ancho, alto, tamaño de pixel, ...
Los drivers de escritura diseñan su propia ventana de propiedades en java. Para ello tiene un método getXMLPropertiesDialog que dice como será esta ventana. Para ello debe devolver una cadena de texto con un XML que contiene esta configuración. El Xml deberá comenzar definiendo el tamaño de la ventana de esta forma:
<window sizex="340" sizey="180"> ... Cuerpo de la ventana ... </window>
Dentro del cuerpo de la ventana deberán definirse los panels con los componentes. Solo está permitido un nivel de anidamiento y el uso de Checkbox, labels, combos, sliders.
<panel sizex="330" sizey="170" layout="FlowLayout" border="yes" > </panel> <label>Tamaño bloque: </label> <combo ident="BLOCKSIZE" selected="64"> <elem>32</elem> <elem>64</elem> <elem>128</elem> </combo> <check ident="GEOREF" selected="10”> </check>
Las opciones del driver que se incorporan pueden ser almacenadas y consultadas en una clase que herede de WriteSupportOptions. WriterSupportOptions contiene todas las opciones de salvado comunes a todos los drivers. Por ejemplo, el driver de escritura de ecw tendrá una clase EcwSupportOptions con las opciones especificas de salvado.
El salvado a raster puede realizarse en dos modos:
El primero se hace a partir de un GeoRasterFile leyendo datos de una imagen raster soportada de entrada y salvandolos en la imagen de la salida. Este método es rapido y directo para la conversión de unos formatos raster a otros. Para esta forma se usa el método fileWrite.
El segundo se realiza salvando la imagen a partir de los datos pasados por un cliente utilizando el método dataWrite. En la construcción del filtro se especificará el tamaño de la imagen de salida, nombre de la misma, coordenadas de georeferenciación, compresión si la hubiere, ... Cuando se invoca el método dataWrite solicitará datos al cliente cuando vacie su buffer hasta que haya terminado con toda la imagen. De esta forma es el escritor el que controla el flujo de bytes y el cliente puede salvar a raster cualquier dato que quiera y que venga de otras fuentes. De esta forma podriamos tener una aplicación con una vista que deseemos salvar a raster, si somos capaces de leer los bytes de ella podemos crear un flujo de datos para salvarlos.
El cliente debe crear una clase servidora de datos. Un ejemplo de esto es la clase Rasterizer que debe implementar el interfaz IDataWriter que obliga al método readData. Este servidor de datos se pasará en el constructor del driver de escritura y lo utilizará para solicitar los datos. Por esto, es el cliente el encargado de controlar los datos que ha pasado y los que le quedan por pasar. El driver irá cogiendo hasta que complete la ventana que necesita.
En la clase Rasterizer a partir de una capa de PxRaster (PxLayerList) se crea calcula el recuadro del trozo de la vista que se necesita en cada petición, se leen los datos desde un image y se devuelven en forma de array de enteros. Será el driver el que escriba a disco ese array. En general, si se necesita hacer uso de salvar a raster se deberá escribir una clase como Rasterizer para el caso concreto que estamos tratando. GvSIG hace uso de esta funcionalidad a través de la clase RasterizerLayer en el paquete com.iver.cit.gvsig, la cual sirve los datos para salvar a raster una vista.
Normalmente la escritura de las imagenes debe ser por franjas. Cada franja tiene la anchura total de la imagen y una altura que viene definida por el tamaño de bloque que el usuario podría variar en la ventana de propiedades del driver.
3.
Filtros
3.1 Creación
de un nuevo filtro
Crear una nueva clase abstracta que heredará de RasterFilter y que tendrá como nombre el nombre del filtro (en inglés a poder ser) seguido de Filter. Usaremos como ejemplo la generación del filtro de transparencia. En este caso, esta clase abstracta debe llamarse TransparencyFilter.
Crear una clase que hereda de esta clase que hemos creado para cada tipo de dato básico que se da soporte. En nuestro caso dos clases heredan de TransparencyFilter y son TransparencyImageFilter y TransparencyShortFilter. Notese que la nomenclatura hace referencia al tipo de dato que maneja. La primera funciona para objetos Image de java y la segunda para imágenes de 16 bits.Estas últimas están representadas por una clase llamada RasterBuf. Si fuera necesario debería hacerse para Float, Double, ...
El constructor de los filtros es recomendable que esté vacio.
En la clase base del filtro que estamos creando (TransparencyFilter) deben ponerse las variables de instancia que contengan los parámetros necesarios para el filtro. En este caso tendriamos 3 vectores bidimensionales que contendran los intervalos de valores a poner como transparentes y cuatro enteros que representan el alpha y el color de transparencia en caso de querer proporcionar alguno. Por defecto será blanco, es decir totalmente transparente y sin ninguna tonalidad.
Aquí deben ponerse también todas las funcionalidades comunes a todos los tipos de filtro de transparencia. Como TransparencyFilter hereda de RasterFilter que es abstracta y tiene métodos abstractos obligará a implementar estos. Si es necesario que estos métodos abstractos tengan código podemos implementarlos aquí sino podrá hacerse en las clases hijas que contienen la especificación para el filtro de transparencia sobre cada tipo de dato concreto. Los métodos abstractos que es preciso implementar son:
abstract public void pre(); .
Aquí pondremos las operaciones que hay que realizar antes de ejecutar el filtro que estamos creando. En nuestro caso está implementado en la clase base del filtro y en las hijas . En la clase base tiene código común para los filtros de transparencia de cualquier tipo de datos.
public void pre(){ this.rangesR = (int[][])params.get("red"); this.rangesG = (int[][])params.get("green"); this.rangesB = (int[][])params.get("blue"); this.alpha = ((Integer)params.get("alpha")).intValue(); this.transparencyColorRed = ((Integer)params.get("transparencyRed")).intValue(); this.transparencyColorGreen = ((Integer)params.get("transparencyGreen")).intValue(); this.transparencyColorBlue = ((Integer)params.get("transparencyBlue")).intValue(); }
En las clases hijas se pone la implementación concreta para ese tipo de dato. En el caso de la transparencia sobre un Image obtenemos el parámetro que contiene la Image y asignamos los valore de altura y anchura de raster a partir de este Image. Antes de finalizar llama al pre() de TransparencyFilter.
public void pre(){ this.image = (Image)params.get("raster"); height = image.getHeight(null); width = image.getWidth(null); super.pre(); }
abstract public void post();
Aquí se ponen las operaciones a realizar después de ejecutar el filtro. En el caso que estamos viendo no es necesario hacer nada por lo que estará vacio. Podria ser necesario alguna operación como por ejemplo cargar el resultado de la operación en alguna variable de salida o algo así.
abstract public void process(int x, int y);
Este método es el encargado de procesar el filtro para un pixel de la imagen. La clase abstracta RasterFilter de la cual heredan todos los filtros tiene el metodo execute() que hará lo siguiente:
pre(); for (int y=0; y<height; y=y+incY) for (int x=0; x<width; x=x+incX) { process(x, y); } post();
Por esto debemos tener en process el código que ejecuta el filtro. Esto siempre suele rellenarse en las clases que representan a un filtro de un tipo de dato concreto. Para nuestro ejemplo en TransparencyImageFilter el process tendrá el siguiente código:
public void process(int x, int y) { int pt = ((BufferedImage) image).getRGB(x,y); int []px4 = {(pt & 0xff000000) >> 24,(pt & 0xff0000) >> 16, (pt & 0x00ff00) >> 8, (pt & 0x0000ff)}; if(rangesR!=null) processRange(rangesR, 1, px4); if(rangesG!=null) processRange(rangesG, 2, px4); if(rangesB!=null) processRange(rangesB, 3, px4); ((BufferedImage) image).setRGB(x,y, ( (px4[0] << 24) & 0xff000000 | (px4[1] << 16) & 0x00ff0000 | (px4[2] << 8) & 0x0000ff00 | (px4[3] & 0x0000ff) )); }
Esta función obtendrá el pixel del buffer lo procesará y salvará el resultado sobre el mismo buffer.
abstract public void processLine(int y);
Esta función realizará el mismo proceso que process(int x, int y) pero aplicada directamente a una línea del buffer.
abstract public int getInRasterDataType();
Devuelve el tipo de dato del buffer de entrada. La clase RasterBuf tiene constantes que tiene todos los tipos de datos posibles por lo que puede realizarse algo así:
public int getInRasterDataType(){ return RasterBuf.TYPE_IMAGE; }
abstract public int getOutRasterDataType();
Devuelve el tipo de dato del buffer de salida. La clase RasterBuf tiene constantes que tiene todos los tipos de datos posibles por lo que puede realizarse algo así:
public int getOutRasterDataType(){ return RasterBuf.TYPE_IMAGE; }
abstract public Object getResult(String name);
Obtiene el resultado del filtro en un Object. Para esto se la pasa un parámetro con la clave del resultado y nos devolverá el Object correspondiente. Esto es necesario porque un mismo filtro puede tener varias salidas, por ejemplo puede tener un raster con el resultado de aplicar el filtro y una clase con algunas estadisticas calculadas en el proceso. En este caso el filtro de transparencia solo devuelve un raster de salida por lo que se hará una función por tipo de dato tal que:
public Object getResult(String name){ if(name.equals("raster")) return (Object)this.image; else return null; }
Esta es la que se hará para TransparencyImageFilter ya que devuelve un tipo Image.
3.2
Gestión de un nuevo filtro
Para el almacenaje de filtros seleccionados hay una clase llamada RasterFilterStack que contiene la pila de filtros y que es generica para cualquier tipo de filtro realizado, es decir, en condiciones normales, en la creación de un nuevo filtro esta clase no debe tocarse. Sin embargo si debe añadirse la gestión del nuevo filtro con filterStackManager . Para añadir este nuevo filtro deberá crearse una nueva clase que heredará de RasterFilterStackManager. RasterFilter esla encargada de añadir filtros a la pila ya que ella es la que sabrá el orden en el que deben ir estos . Un orden de la pila incorrecto da un resultado de aplicaciones de filtros indeseado. Con esto deducimos que la pila es tratada como tal para la ejecución de filtros pero que estos deben estar de antemano ordenados correctamente por lo que no es una pila en el sentido estricto.
Este nuevo gestor de filtros deberá implementar la interfaz StackManager de forma que la definición de nuestra nueva clase sería más o menos asi:
public class PruebaStackManager extends RasterFilterStackManager implements StackManager{ ... }
Para el nuevo filtro debe añadirse en esta nueva clase una constante que represente el tipo del nuevo filtro. Lo más lógico es darle un número de orden consecutivo a las que hay pero podría asignarse otro en caso de haber una causa justificada. Las actualmente asignadas en la RasterFilterStackManager son
transparency = 0; enhanced=1; computeminmax=2; tail=3;
El vector order de RasterFilterStackManager contiene la forma de ordenación de los filtros de la pila. Este orden es importante, por ejemplo la transparencia debe aplicarse despues del realce de la imagen ya que esta se aplica a rangos de colores y antes o después del realce los rangos de colores difieren por lo que la transparencia debe aplicarse sobre la imagen realzada y no al revés. Para asignar este nuevo order deberemos indicarlo en el constructor de esta clase que hemos creado.
public PruebaStackManager(RasterFilterStack filterStack){ super(filterStack); addTypeFilter("prueba", PruebaStackManager.prueba, 2); }
Habrá que elegir una posición para el nuevo filtro creado para que produzca el resultado deseado. La función addTypeFilter tendrá como parámetros en nombre del filtro, constante asignada y posición para la ordenación. Si introducimos el nuevo filtro en la posición 3 la ordenación inicial:
computeminmax, tail, enhanced, transparency
quedará
computeminmax, tail, prueba, enhanced, transparency
ya que la posición en el vector de ordenación tiene en cuenta la posición 0.
En RasterFilterStackManager también existe una función llamada getType que devuelve el tipo de filtro que contiene un RasterFilter. Deberemos añadir el nuestro para una correcta gestión. Para ello sobrescribiremos el método getType de esta forma:
public int getType(RasterFilter rasterFilter){ if(rasterFilter instanceof <<NuestoFiltro>>) return PruebaStackManager.<<Nuestra constante>>; return super.getType(rasterFilter); }
sustituyendo <<NuestroFiltro>> por el nombre de la clase abstracta base del filtro que hemos construido y <<Nuestra constante>> por la constante que representa nuestro filtro.
Dentro de nuestra clase habrá que definir una función para añadir el nuevo filtro. Esta función debe tener como argumentos los parámetros necesarios para el nuevo filtro. En el caso de un filtro de transparencia podrian ser los intervalos para RGB y el color de transparencia.
En esta función hay que hacer algunas acciones obligatorias y otras opcionales. Podemos verlas sobre un ejemplo:
Cabecera de la función ya comentada:
public void addTransparencyFilter( int[][] red, int[][] green, int[][] blue, int alpha, int transparencyRed, int transparencyGreen, int transparencyBlue){
Es necesario crear un RasterFilter de un tipo u otro dependiendo del tipo de dato que nos diga la pila que necesita. El método filterStack.getDataTypeInFilter(RasterFilterStackManager.transparency) devuelve el tipo de dato que necesitamos si el filtro que vamos a meter es de transparencia. Esta función de la pila calculará en que posición debe ir el filtro y que tipo de dato devuelve el que tendrá por encima, por lo tanto sabremos de que tipo es el filtro que debemos crear. Podemos hacer la selección con un switch de esta forma:
RasterFilter filtro = null; switch(filterStack.getDataTypeInFilter(((Integer)typeFilters.get("transparency")).intValue())){ case RasterBuf.TYPE_IMAGE:filtro = new TransparencyImageFilter();break; case RasterBuf.TYPE_SHORT: case RasterBuf.TYPE_USHORT: case RasterBuf.TYPE_INT:filtro = new TransparencyShortFilter();break; }
Si tiene parámetros deberemos añadirlos al filtro creado con addParam. Cada parámetro debe ser añadido con una clave. Esta debe coincidir con la que definimos para su recuperación en el método pre() del filtro.
if(red != null)filtro.addParam("red", red); if(green != null)filtro.addParam("green", green); if(blue != null)filtro.addParam("blue", blue); filtro.addParam("alpha", new Integer(alpha)); filtro.addParam("transparencyRed", new Integer(transparencyRed)); filtro.addParam("transparencyGreen", new Integer(transparencyGreen)); filtro.addParam("transparencyBlue", new Integer(transparencyBlue));
En este momento podriamos añadir el filtro a la pila con addFilter si no necesitara ninguna restricción. En este caso tenemos que añadir código para comprobar que si hay filtros equivalentes no será necesario añadir el nuevo o si hay varios filtros de transparencia que están contenidos en el nuevo tendremos que eliminar estos y añadir el nuevo.
//Elimina los filtros que son equivalentes a este for(int i=0;i<filterStack.lenght();i++){ if( filterStack.get(i) instanceof TransparencyImageFilter || filterStack.get(i) instanceof TransparencyShortFilter){ //Si este filtro es equivalente a uno de la pila se elimina este if(((TransparencyFilter)filtro).isEquivalent((TransparencyFilter)filterStack.get(i))) filterStack.removeFilter(filterStack.get(i)); } } //Añade el filtro si no hay uno equivalente boolean equivalentFilter = false; for(int i=0;i<filterStack.lenght();i++){ if( filterStack.get(i) instanceof TransparencyImageFilter || filterStack.get(i) instanceof TransparencyShortFilter){ //Si no existe en la pila un filtro equivalente se añade if(((TransparencyFilter)filterStack.get(i)).isEquivalent((TransparencyFilter)filtro)){ equivalentFilter = true; break; } } } if(!equivalentFilter) filterStack.addFilter(RasterFilterStackManager.transparency, filtro); }
Teniendo la nueva función para añadir filtro ya podremos añadirlo desde un dialogo creando un RasterFilterStackManager o usando uno ya creado y leyendo los parámetros del filtro desde el cuadro (también podemos ponerlos fijos si nos interesa). En nuestro caso:
stackManager.addTransparencyFilter( contentPane.getRangeRed(), //Parámetros leidos desde el dialogo contentPane.getRangeGreen(), contentPane.getRangeBlue(), 0x10, //Parámetros a valor fijo 0xff, 0xff, 0xff);
Para que sea posible salvar el estado de un raster cuando se salva un proyecto y se le hayan aplicado filtros es necesario codificar esta posibilidad para ello deberemos crear dos métodos obligados por el interfaz en nuestro Manager. Estos son:
public ArrayList getStringsFromStack(RasterFilter rf); y public void createStackFromStrings(ArrayList f, Integer pos);
En el primero es para añadir las cadenas al XML que salva el proyecto y habrá que comprobar si primero si el RasterFilter pasado es instancia de este filtro que estamos implementando y si lo es añadir las cadenas adecuadas de esta forma:
public ArrayList getStringsFromStack(RasterFilter rf){ if(rf instanceof PruebaFilter){ filterList.add("filter.prueba.active=true"); } }
El segundo es para recuperar el estado de un proyecto. Para ello lo primero que deberemos hacer es recuperar del array el elemento analizado, comprobar que contiene la cadena que representa nuestro filtro y si es así eliminar esa entrada del vector ya que ya ha sido analizada y añadir las acciones que conllevan la adición de nuestro filtro. En este caso un simple addPruebaFilter añadirá el filtro creado cuando se habra un proyecto con la cadena filter.prueba.active=true.
public void createStackFromStrings(ArrayList f, Integer pos){ String fil = (String)f.get(pos); if(fil.startsWith("filter.prueba.active") && getValue(fil).equals("true")){ filters.remove(pos.intValue()); this.addPruebaFilter(); pos = -1; } }
Cresques tiene incluidas
interfaces gráficas para la gestión de propiedades y
filtros y el salvado a raster. Estas interfaces no tienen
funcionalidad completa sino que son paneles con los controles para
una recogida de datos desde la aplicación cliente. El cliente
debe insertar estos paneles dentro de sus propios frames y gestionar
la recogida y escritura de datos.
Para la gestión de los controles de propiedades, el cliente deberá crear una clase que herede de org.cresques.ui.raster.FilterRasterDialogPanel para tener el acceso a los controles protected que tiene. Por ejemplo, podemos querer incluir los paneles dentro de un frame con un botón de Aceptar, otro de Cancelar y otro de Aplicar. Un ejemplo de lo que aquí se explica puede encontrarse en gvSIG dentro de la clase com.iver.cit.gvsig.gui.panels.PropertiesRasterDialog.
Si se desea gestionar la restauración de valores cuando se pulsa cancelar deberá hacerlo en esta clase. Una posibilidad es crear una clase, por ejemplo Status que guarde los valores al entrar y los restaure al salir en caso que las acciones sean canceladas.
Para la traducción del panel habrá que crear una función normalmente llamada por el constructor que sustituya las siguientes cadenas por el idioma que se desee:
this.getBandSetup().getFileList().getJButtonAdd().setText("Anadir"); this.getBandSetup().getFileList().getJButtonRemove().setText(“Eliminar"); this.getBandSetup().getFileList().lbandasVisibles.setText("bandas"); RasterTransparencyPanel tpan = this.getTransparencyPanel(); tpan.lGreenValue.setText("Valor verde:"); tpan.lRedValue.setText("Valor rojo:"); tpan.lBlueValue.setText("Valor azul:"); tpan.getTransparencyCheck().setText("transparencia"); tpan.getOpacityCheck().setText("opacidad"); tpan.lRange.setText("usar_rango: 1,3,5:8"); tpan.lPixelValue.setText("valor pixel: 0 a 255"); EnhancedPanel ep = this.getEnhancedPanel(); ep.lLineal.setText("lineal directo"); ep.lQueue.setText("recorte colas"); ep.lWithoutEnhanced.setText("sin realce"); ep.lCut.setText("% "recorte"); ep.lRemove.setText("eliminar extremos"); for(int i=0;i<this.getTab().getTabCount();i++){ if(this.getTab().getTitleAt(i).equals("Info")) this.getTab().setTitleAt(i,"info"); if(this.getTab().getTitleAt(i).equals("Transparencia")) this.getTab().setTitleAt(i,"Transparencia"); if(this.getTab().getTitleAt(i).equals("Bandas")) this.getTab().setTitleAt(i,"bandas"); if(this.getTab().getTitleAt(i).equals("Realce")) this.getTab().setTitleAt(i,"realce"); } this.getAcceptButton().setText("Aceptar"); this.getApplyButton().setText("Aplicar"); this.getCancelButton().setText("Cancelar");
En la gestión de eventos del botón Aceptar hay que controlar el estado de cada panel a acceder a los métodos de estos para recuperar la información y poder procesarla. Cada panel tiene los suyos:
Panel de realce:
this.getEnhancedPanel(): Recupera el panel de tipo EnhancedPanel.
panel.getLinealDirectoRadioButton(): Obtiene el control de selección de filtro de realce.
panel.getTailCheck(): Obtiene el control de selección de recorte de colas.
panel.getRemoveCheck(): Obtiene el control de eliminar extremos.
Panel.getTailText(): Obtiene el porcentaje de recorte del cuadro de texto.
Panel de transparencia:
this.getTransparencyPanel(): Recupera el panel de tipo TransparencyPanel.
panel.getOpacityCheck(): Obtiene el control de opacidad activada (nivel de traslucidez de la imagen).
panel..getTransparencyCheck(): Obtiene el control de transparencia activada (transparencia por rangos de pixeles).
this.getRangeRed(): Obtiene los rangos de colores de pixeles de transparencia para la banda del rojo como vector de enteros.
this.getRangeGreen(): Obtiene los rangos de colores de pixeles de transparencia para la banda del verde como vector de enteros.
this.getRangeBlue(): Obtiene los rangos de colores de pixeles de transparencia para la banda del azul como vector de enteros.
Panel de bandas:
this.getBandSetup(): Recupera el panel de tipo BandSetup
this.getAssignedBand(GeoRasterFile.RED_BAND): Obtiene la banda asignada al rojo.
this.getAssignedBand(GeoRasterFile.GREEN_BAND): Obtiene la banda asignada al verde.
this.getAssignedBand(GeoRasterFile.BLUE_BAND): Obtiene la banda asignada al azul.
4.2 Interfaz de salvar a raster
Para la gestión de los controles de raster, el cliente deberá crear una clase que herede de org.cresques.ui.raster.SaveRasterDialogPanel para tener el acceso a los controles protected que tiene. Por ejemplo, podemos querer incluir los paneles dentro de un frame con un botón de Aceptar y otro de Cancelar. Un ejemplo de lo que aquí se explica puede encontrarse en gvSIG dentro de la clase com.iver.cit.gvsig.gui.panels.SaveRasterDialog.
Para la traducción del panel habrá que crear una función normalmente llamada por el constructor que sustituya las siguientes cadenas por el idioma que se desee:
DataInputSaveRaster dInput = ((SaveSetupPanel)super.getContentPanel()).getSaveParameters(); dInput.lSupIzq.setText("lsup izq :"); dInput.lInfDer.setText("linf der :"); dInput.lFile.setText("Fichero :"); dInput.lResolucion.setText("resolucion"); dInput.lEscala.setText("escala 1:"); dInput.lPpp.setText("ppp"); dInput.bSeleccion.setText("Seleccionar"); dInput.lAncho.setText("ancho."); dInput.lAlto.setText("alto.");
En la gestión de eventos del botón Aceptar hay que controlar el estado de cada panel a acceder a los métodos de estos para recuperar la información y poder procesarla.
(DataInputSaveRaster)((SaveSetupPanel)((SaveRasterDialogPanel)this.getContentPane()).getContentPanel()).getSaveParameters(): Obtiene el dialogo de tipo DataInputSaveRaster
dialog.getTinf_derX().getText(): Obtiene la coordenada X inferior derecha.
dialog.getTinf_derY().getText(): Obtiene la coordenada Y inferior derecha.
dialog.getTsup_izqX().getText(): Obtiene la coordenada X superior izquierda.
dialog.getTsup_izqY().getText(): Obtiene la coordenada Y superior izquierda.
((SaveSetupPanel)((SaveRasterDialogPanel)this.getContentPane()).getContentPanel()).getFileName(); Obtiene el nombre del fichero sobre el que se salvará.
dialog.getBPropiedades(): Obtiene el botón de propiedades.
El botón de propiedades de raster debe de capturarse el evento de pulsado para ser procesado. Dependiendo del tipo de driver cargado hará una acción u otra. El tipo de driver puede determinarse a través del texto del control, ya que este cambia cuando hemos seleccionado una extensión de raster con “Seleccionar”. Este botón de propiedades mostrará el dialogo de propiedades del driver. La configuración de esta ventana es leida directamente desde el mismo driver a través de un texto en formato XML. Para esto crearemos la ventana en una clase, por ejemplo SaveRasterPropsDialog que puede heredar de Jdialog en cuyo constructor crearemos el parser del XML y asignaremos el tamaño a nuestra ventana de esta forma:
String xml = writer.getXMLPropertiesDialog(); if(xml!=null){ CXMLParser parser = new CXMLParser(xml); widthWindow = Integer.parseInt(parser.getAttr("window","sizex")); heightWindow = Integer.parseInt(parser.getAttr("window","sizey")); } setContentPane(getContentPane());
donde el writer es el driver de tipo GeoRasterWriter y contentPane es un SaveRasterPropsDialogPanel. El panel de propiedades puede añadirse de forma facil con una función como esta:
public Container getContentPane() { if (contentPane == null) { contentPane = new SaveRasterPropsDialogPanel(writer); } return contentPane; }
Este dialogo puede tener botones de aceptar y cancelar cuyos eventos de pulsado deben ser capturados.