Personal tools
You are here: Home Members Joaquin del Cerro gvSIG Scripting - raster Notas 4 201307171149 - Re: Re: Some doubts [GSoC - 2013]

El 17/07/13 11:49, sandeep kumar escribió:

> Hi sir,
> 
>       I have some small doubts about the documentation you provided
> recently.
> 
>    1. I observed the patch you sent. I think the reason for checking if it
>       is an instance of FLyrVect  is to load the layer as is if it is Raster
>       layer else load it with Layer class. If so, Why do we load a vector layer
>       with Layer class and not a raster layer? Correct me if i understood the
>       patch wrongly.

Holas.

Cuando abordamos la implementacion del API de scripting para acceso vectorial, vimos que lo mas facil podia ser tener un recubrimiento para las clases java en jython añadiendoles a estas los metodos que nos podian hacer falta.

El problema es que no podemos extender de las clases java, ya que los objetos los crea la parte java y nunca se crearian los objetos jython.

Esto nos planteo algun problema ya que cada vez que tenemos que pasarle a una funcion java un objeto recuvierto en jython el usuario debe sere consciente que ha de hacer algo especial y no puede pasarlo directamente.

Despues de aquello hemos aprendido algo mas sobre jython. Para python, una clase es como cualquier otro objeto. Puedes coger el objeto clase y añadirle nuevos metodos y atributos al vuelo. Jython, no es tan flexible, pero si que nos permite añadirle metodos a la clase.

Imagina la clase String de java y que queremos añadirle un nuevo metodo "hello".

$ jython
Jython 2.2.1 on java1.7.0_21
Type "copyright", "credits" or "license" for more information.
>>> from java.lang import String
>>> 
>>> s=String("this is a test")
>>> s.hello()
Traceback (innermost last):
  File "<console>", line 1, in ?
AttributeError: hello
>>> def hello(self):
...   return "Hello '" + self.toString() + "'"
... 
>>> String.hello = hello
>>> s.hello()
"Hello 'this is a test'"
>>> 

Como ves, es simple añadir metodos a una clase java. La clase sigue siendo la de antes. Si tenemos una instancia de String podemos pasarsela a cualquier metodo java que reciva un String.

Para inyectar el nuevo metodo solo hemos tenido que crear una funcion "hello", que recibe como primer parametro la instancia del String ("self"), y luego asignarle esa funcion directamente a la clase String.

Cuando tengamos tiempo pasaremos la implementacion de la parte de script vectorial al nuevo formato. Injectar metodos a las clases java en lugar de recubrirlas.

En lo que estas haciendo, directamente prefiero optar por la inyeccion de metodos, por eso no es exactamente igual a lo que hay en vectorial.

>       2. I think the __init__ method of RasterLayerExtension is incomplete.
>       What should go into it?

El metodo __init__ es el constructor de la clase. Simplemente inicializa los atributos de esta.

def __init__(self, store=None):
  self.store = store
  self.buffer = None
  self.query = None
  self.values = None
  self.kernel = None
  self.setElem = None
  self.getElem = None
>       3. In the createNewBuffer method of RasterLayerExtensions while
>       creating params you used a method 'getBufferFactory'  for RasterManager
>       Instance. But there is no such method for RasterManager.

Me temo que ahora mismo no puedo mirar esto... en cuanto pueda le hecho un vistazo.

>       4. In prepareBuffer method of RasterLayerExtensions class, while
>       creating kernel in the last, we have several lines of code. I didn't get
>       exactly what it's purpose is.

Cada vez que se itera por el raster se precisa una tupla para los valores de las distintas bandas de un punto, o para la matriz de 3x3 de estos valores si se usan los metodos de "kernel". Como estamos hablando de miles (30.000+) puntos habia pensado crearlas una sola vez en la creacion del buffer en lugar de crear una para cada punto.

Oh!. hay un error en el codigo, me he dejado un "]". Deberia decir:

self.kernel = [[self.values] * 3 ] * 3
$ jython
Jython 2.2.1 on java1.7.0_21
Type "copyright", "credits" or "license" for more information.
>>> bandcount=3
>>> values = [0]*bandcount
>>> values
[0, 0, 0]
>>> kernel=[[values]*3]*3
>>> kernel
[[[0, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]]]
>>> 
>       5. What do you exactly mean by injecting new method to FLyrRaster
>       Class.? Does it just mean adding new methods to this class? If
> so, why are
>       we adding them to FLyrRaster class?

Mas arriva he comentado ya sobre la inyeccion de metodos.

>       6. I understood the methodology of the walk, filter methods. But what
>       do you exactly mean by Filter kernel, operation kernel, walk kernel?

Los metodos '*'Kernel, se comportan de forma similar a sus analogos. Es bastante corriente a la hora de manipular un raster, que en lugar de tomar como valor de entrada unicamente el valor de un punto, tomemos los valores de ese punto y los de alrededor. Para poder trabajar con algoritmos que precisan esto es para lo que he introducido esos metodos.

> 
>   I understood the entire code you provided. I was really worried about the
> way you organized the code. Some pieces i get this doubt of why it is
> placed here? why not in the classes itself etc (getBandCount,getWidth etc
> are outside the classes). But i think i will get a hold of it once i start
> testing the code.

No hay que perder de vista que lo que se pretende es adaptar una libreria de acceso a raster ya existente hecha en java para que sea facilmente usable por usuarios que quieran hacer pequeños scripts. La libreria java de acceso a raster ya esta hecha. No debemos modificarla, aunque el desarrollo de esta este dentro del core de gvSIG, desde scripting la usamos como una libreria mas de terceros. Con esto en mente, hay varias forma de hacerlo como ya he comentado mas arriba, recubriendo las clases java o injectando metodos en estas.

Otra cosa es que te llame la atencion la forma en la que desde jython se inyectan metodos en una clase. Por un lado se define la funcion que tendra la implementacion del metodo, y luego se le asigna a la clase java.

Relacionado con esto, he estado haciendo unas pruebas y acabaremos teniendo una clase jython con los metodos en lugar de estar sueltos en el modulo, pero unicamente a efectos de facilitar la generacion de documentacion.

> 
>  Does my work now is to make the code work with appropriate methods
> provided and a user should be able to import the file we provided?
> 

De momento, nos centraremos en hacer funcionar el codigo, incluyendo al final de el codigo de testing. Una vez este listo ya lo integraremos como un modulo que un usuario pueda importar.

> Note: Could you provide some detailed information about *Store, Buffer and
> Query?* I went through the documentation but couldn't get much of it. Also,
> How should i plan to continue with the documentation. ? Should i start
> working on the documentation?
> 

Recapitulemos lo que tenemos y lo que habria que hacer.

Tenemos esbozado un API para manipular raster con aproximadamente una docena de metodos. Deberiamos tener claro que ha de hacer cada uno de ellos, sus datos de entrada y lo que generan. Con esto claro tendremos que:

  • Generar la documentacion.
  • Implementarlos
  • Preparar una serie de scripts que nos valgan tanto para probar nuestra implementacion como de ejemplo para los usuarios.

Respecto a la generacion de documentacion tambien hemos tenido problemas con lo que hay ahora mismo para la parte de scripting vectorial. Antes se creo por un lado el codigo y por otro la documentacion. El codigo tenia pytondocs pero no teniamos ninguna herramienta que fuese capaz de extraerlos y generar documentacion aceptable. No es practico mantener la documentacion separada del codigo, asi que ahora probaremos otra cosa.

He hecho unas pruebas este fin de semana y creo que lo mejor sera utilizar doxygen para generar y mantener la documentacion. Usa una variante de pythondocs (similares a los javadocs).

Habra que adaptar ligeramente el codigo para que se generen correctamente la documentacion.

Crearemos una clase LayerRaster que usaremos "unicamente" como contenedor de las funciones a usar para inyectar en FLyrRaster y asi poder estructurar la documentacion de forma adecuada. Declararemos los metodos de la clase como estaticos ya que para inyectarlos en la clase java no pueden ser metodos de una clase jython.

De momento, rellena los pythondocs para doxygen adecuadamente y no te preocupes por generar la documentacion en formato HTML.

Incluyo aqui la parte de codigo con los pythondocs de doxygen. Seria algo asi

...

#--------------------------------------------------------------
# This code will go to the module gvsig_raster.py 
# Now is here to debug more easily
# Begin module gvsig_raster

##
#
# This module add raster support to the gvSIG scripting module.
#
# TODO: Add here the documentation you need
#

from org.gvsig.fmap.dal.coverage import RasterLocator
from org.gvsig.fmap.dal import DALLocator
from org.gvsig.fmap.dal.coverage.dataset import Buffer, BufferParam
from org.gvsig.raster.fmap.layers import FLyrRaster
from java.lang import Byte, Short, Integer, Float, Double

from os.path import splitext

##
#
#  Load a raster file in a layer
#
def loadRasterLayer(rasterfile ,mode="r"):
  if not isinstance(rasterfile, File):
    rasterfile = File(rasterfile)

  name, ext = splitext(rasterfile.getName())[0]
  
  # Get the manager to use
  dalManager = DALLocator.getDataManager()

  if ext.lower() == ".ecw" or ext.lower() == ".jp2" :
    # FIXME
    pass
  elif ext.lower() == ".mrsid":
    # FIXME
    pass
  else:
    # Create the parameters to open the raster store based in GDAL
    params = dalManager.createStoreParameters("Gdal Store")
    params.setFile()

  # Create the raster store "maven-doxygen-plugin"
  dataStore = dalManager.createStore(params)

  # Create a raster layer based in this raster store
  layer = mapContextManager.createLayer(name, dataStore);

  return layer

## @cond FALSE

rasterLayerExtensions = dict()

class RasterLayerExtensions(object):
  """This class hold aditional properties and operations need to manage the scripting raster layer
  (query, buffer, values....)
  """
  def __init__(self, store=None):
    self.store = store
    self.buffer = None
    self.query = None
    self.values = None
    self.kernel = None
    self.setElem = None
    self.getElem = None

  def prepareQuery(self):
    ## See RasterManager in javadocs for more info
    self.query = RasterLocator.getManager().createQuery();
    ## See RasterQuery in javadocs for more info
    self.query.setAllDrawableBands()
    self.query.setAreaOfInterest()
    self.buffer = None
    self.values = None
    self.kernel = None

  def createBuffer(self):
    self.buffer = self.store.query(self.getQuery())

  def createNewBuffer(with,height,bandcount, datatype):
    if self.store != None:
      raise RuntimeException("Can't create a new buffer associated to a store")
      
    # FIXME: workaround to work with a jython bug passing byte, short and 
    # double values as parameters
    if datatype in (Buffer.TYPE_BYTE, Buffer.TYPE_SHORT, Buffer.TYPE_INT):
      datatype = Buffer.TYPE_INT
    else:
      Buffer.TYPE_FLOAT
    # End workaround
    
    params = RasterLocator.getManager().getBufferFactory().createBufferParams(
      width, 
      height,
      bandcount,
      datatype, 
      BufferParam.CACHED
    )
    self.buffer = RasterLocator.getManager().getBufferFactory().createBuffer(params)
    self.prepareBuffer()
        
  def prepareBuffer(self):
    def setElemByte(buffer, line, col, band, data):
      buffer.setElem(line, col, band, Byte(data).byteValue())

    def setElemShort(buffer, line, col, band, data):
      buffer.setElem(line, col, band, Short(data).shortValue())
    
    def setElemInt(buffer, line, col, band, data):
      buffer.setElem(line, col, band, Integer(data).intValue())
    
    def setElemFloat(buffer, line, col, band, data):
      buffer.setElem(line, col, band, Float(data).floatValue())
    
    def setElemDouble(buffer, line, col, band, data):
      buffer.setElem(line, col, band, Double(data).doubleValue())
      
    t = buffer.getDataType()
    if t == Buffer.TYPE_BYTE:
      this.getElem = this.buffer.getElemByte
      this.setElem = setElemByte
    elif t == Buffer.TYPE__SHORT or t == Buffer.TYPE__USHORT:
      this.getElem = this.buffer.getElemShort
      this.setElem = setElemShort
    elif t == Buffer.TYPE__INT:
      this.getElem = this.buffer.getElemInt
      this.setElem = setElemInt
    elif t == Buffer.TYPE__FLOAT:
      this.getElem = this.buffer.getElemFloat
      this.setElem = setElemFloat
    elif t == Buffer.TYPE__DOUBLE:
      this.getElem = this.buffer.getElemDouble
      this.setElem = setElemDouble
    self.values = [0] * this.buffer.getBandCount()
    self.kernel = [[self.values] * 3 ] * 3

  def getQuery(self):
    if self.query == None:
      self.prepareQuery()
    return self.query
      
  def getBuffer(self, store):
    if self.buffer == None:
      self.createBuffer()
      self.prepareBuffer()
    return self.buffer

  def getValue(self, band, row, column):
    if self.getElem == None:
      self.createBuffer()
      self.prepareBuffer()
    return self.getElem(row,column,band)

  def getBandValues(row, column):
    if self.getElem == None:
      self.createBuffer()
      self.prepareBuffer()
    for b in xrange(self.buffer.getBancCount()):
      self.values[b] = self.getElem(row, column, b)
    return self.values

  def setBandValues(row,column,values):
    for b in xrange(self.buffer.getBancCount()):
      self.setElem(self.buffer, row, column, b, values[b])
    
  def saveBuffer(filename):
    manager = DALLocator.getDataManager()
    eparams = manager.createServerExplorerParameters("FilesystemExplorer")
    eparams.setDynValue("initialpath", "/tmp")
    serverExplorer = manager.openServerExplorer(eparams.getExplorerName(), eparams)
  
    sparams = (NewRasterStoreParameters)serverExplorer.getAddParameters("Gdal Store")
    sparams.setFileName(filename)
    sparams.setBuffer(buffer)
  
    serverExplorer.add("Gdal Store", sparams, True)
## @endcond
  

##
#
# Represents a raster layer.
#
class RasterLayer(FLyrRaster):

  TYPE_BYTE =  Buffer.TYPE_BYTE
  TYPE_SHORT = Buffer.TYPE_SHORT
  TYPE_INT = Buffer.TYPE_INT
  TYPE_FLOAT = Buffer.TYPE_FLOAT
  TYPE_DOUBLE = Buffer.TYPE_DOUBLE
  
  @staticmethod  
  ## @cond FALSE
  def getExtensions(self):
    """This a internal method,a don't use it.
    """
    global rasterLayerExtensions
    extensions = rasterLayerExtensions.get(self.hashCode(),None)
    if extensions == None :
      extensions = RasterLayerExtensions(self.getDataStore())
      rasterLayerExtensions[self.hashCode()] = extensions
    return extensions
  ## @endcond
        
  @staticmethod 
  ##
  #
  # Return the number of bands of the raster
  # 
  # @return the number of band of the raster
  #
  def getBandsCount(self):
    return self.getDataStore().getBandCount()

  @staticmethod  
  ##
  #
  # Return the width in points of the raster
  # 
  # @return the width of the raster
  # 
  def getWidth(self):
    return self.getDataStore().getWidth()
    
  @staticmethod  
  ##
  #
  # Return the height in points of the raster
  # 
  # @return the height of the raster
  # 
  def getHeight(self):
    return self.getDataStore().getHeight()
    
  @staticmethod  
  ##
  #
  # Return the data type of the raster.
  # 
  #  FIXME: Add the documentation about of data types
  #
  # @return FIXME
  #
  def getDataType(self):
    return self.getDataStore().getDataType()

  @staticmethod  
  ##
  # 
  # Return the value of a point of a band for a row/column of 
  # the raster.
  # 
  # This method use with care, it has a strong overhead. Use instead
  # the method "walk" to go over the raster.
  #
  # @param band band from to retrieve the value 
  # @param row FIXME
  # @param column FIXME
  #
  # @return FIXME
  #
  def getData(self, band, row, column):
    return self.getExtensions().getValue(band, row, column)

  @staticmethod  
  ##
  #
  # Go over the raster and for each point call to the funcion
  # "operation" and pass as argument a tuple with the values of
  # the point for each band.
  #
  # This method don't return any value
  #
  # @param  operation FIXME
  #
  def walk(self, operation):
    extension = self.getExtensions()
    store = self.getDataStore()
    for band in xrange(store.getBandCount()):
      for line in xrange(store.getHeight()):
        for column in xrange(store.getWidth()):
          operation(extension.getBandValues(line, column))
        
  @staticmethod  
  ##
  #
  # DOCUMENT ME !!
  # 
  def walkKernel(self, operation):
    pass

  @staticmethod  
  ##
  #
  # DOCUMENT ME !!
  # 
  def filter(self, filter, targetfilename, targetdatatype=None, targetbandcount=None):
    extension = self.getExtensions()
    store = self.getDataStore()
    targetExtension = RasterLayerExtensions()
    targetExtension.createNewBuffer(
      store.getWidth(), 
      store.getHeight(),
      targetbandcount,
      targetdatatype
    )
    for band in xrange(store.getBandCount()):
      for line in xrange(store.getHeight()):
        for column in xrange(store.getWidth()):
          values = filter(extension.getBandValues(line, column))
          targetExtension.setBandValues(line, column, values)
    targetExtension.saveBuffer(targetfilename);
      
  @staticmethod  
  ##
  #
  # DOCUMENT ME !!
  # 
  def filterKernel(self, filter, targetfilename, targettype):
    pass
    
  @staticmethod  
  ##
  #
  # DOCUMENT ME !!
  # 
  def operation(self, oepration, layer2, targetfilename, targettype):
    pass

  @staticmethod  
  ##
  #
  # DOCUMENT ME !!
  # 
  def operationKernel(self, oepration, layer2, targetfilename, targettype):
    pass

#
# Inject new methods in the class FLyrRaster
#
FLyrRaster.getExtensions = RasterLayer.getExtensions
FLyrRaster.getBandsCount = RasterLayer.getBandsCount
FLyrRaster.getWidth = RasterLayer.getWidth
FLyrRaster.getHeight = RasterLayer.getHeight
FLyrRaster.getDataType = RasterLayer.getDataType
FLyrRaster.getData = RasterLayer.getData
FLyrRaster.walk = RasterLayer.walk
FLyrRaster.walkKernel = RasterLayer.walkKernel
FLyrRaster.filter = RasterLayer.filter
FLyrRaster.filterKernel = RasterLayer.filterKernel
FLyrRaster.operation = RasterLayer.operation
FLyrRaster.operationKernel = RasterLayer.operationKernel


#
# end module gvsig_raster.py
#-------------------------------------------------------------

...

Saludos

Joaquin

> 
> 
> Thank You,
> Sandeep Kumar,
> MS by Research,
> Lab for Spatial Informatics,
> IIIT-Hyderabad.
> 

Powered by Plone CMS, the Open Source Content Management System

This site conforms to the following standards: