retour aux mémos     retour au modèle     back to SimMasto home page   retour à la page d'accueil

Implantation d'un SIG (geography) dans Repast Sim-phony


Objectif : réaliser une simulation basique basée sur un support raster.
Entrée : un raster dans un format image sans perte (png ..)
Sortie : un display dans le GUI de Repast Simphony
Outils: Repast-Simphony

Présentation

Les rasters sont très utiles pour représenter des couches de données. On considère que chaque pixel représente une surface et que les valeurs de ces pixels ont une signification (hauteur, température, chlorophylle...).

La solution la plus simple pour utiliser un raster comme support est d'utiliser une grille de même dimension que le raster et d'associer à  chaque cellule un agent parcelle construit à partir de la valeur du pixel correspondant dans le raster. On peut alors positionner les agents à l'intérieur de ces agents parcelles.

Cependant plusieurs problèmes apparaissent rapidement:

  1. Le premier problème est propre aux grilles, les agents se déplaçant en mètre, leur associer un déplacement en case n'est pas évident (surtout quand on peut faire varier la taille d'une case, la vitesse de l'agent et la période représentée par un tick de temps).
  2. Le deuxième problème vient de la lourdeur d'affichage. En effet si l'on souhaite afficher la grille d'agent mobile, le logiciel va parcourir chaque agent terrain à chaque pas de temps pour le redessiner (même si l'agent terrain ne fait rien). Un petit raster 120 pixels par 120 pixels contient 14.400 pixels. Si le simulateur doit redessiner 14.400 agents à chaque pas de temps, la simulation sera très lente.

Pour résoudre ces deux problèmes, nous allons combiner différents procédés.

  1. Pour pouvoir conserver la gestion des agents avec des conversions simples, nous allons utiliser une projection de type espace continu, pour contenir les agents « mobiles ».
  2. Pour conserver les interactions entre les agents et le terrain, nous devons conserver les agents parcelles. Nous allons donc les stocker dans une matrice qui aura les mêmes dimensions que le raster. Mais ces agents parcelles ne seront pas représentés.
  3. Pour conserver une représentation du terrain, nous allons utiliser un objet « GridValueLayer ». Il fonctionne comme une grille normale, mais ne contient que des valeurs simples. Une fois que cet objet sera construit, on pourra l'afficher comme une image raster.

G        En revanche, il va falloir définir une classe de style pour faire correspondre à chaque valeur une couleur.

 

Attention: Il est important de toujours penser à faire correspondre les données des classes java avec le schéma défini dans le fichier model.score. Il faut donc ajouter la projection de type espace continu et le GridValueLayer dans les éléments du contexte défini dans model.score. Il faut également faire en sorte que les libellé se correspondent entre le nom donné dans le fichier model.score et la chaîne de caractères servant à  repérer l'élément dans le code.

L'en tête de la classe Raster_manager aura donc cet aspect:

public class Grid_Manager implements Ground_Manager

{

  private static    Map<Integer,Color> map;

  private double  size_of_one_box = 3;

  GridValueLayer grille = null;

  ContinuousSpace space = null;

  Field_Agent[][] matrice_terrain;

public Context create_ground(Context context,String url,String geographyname )

  {

//we load the data in an integer matrix from the file at the       specified URL

int[][] matrice = img_raster_loader(url);

//building of  the projection's elements

.

.

.

return context;

  }

}

Lecture des données d'un raster

Avant de construire la projection, on va récupérer les données qui nous intéressent dans une matrice :

private int[][]img_raster_loader(String url)

{

int[][] matrice = null ;

int[] pixel = new int[3];

try {

//we get back the bufferedImage which represent a raster

     BufferedImage img =  ImageIO.read(new File(url));

     if (img == null)

     {

           System.out.println(" l'image est null");

     }

//we can take the colormodel if we want to draw the raster with the same color //as the original image

IndexColorModel c =(IndexColorModel)img.getColorModel();

//we transform the colormodel in a map

Map<Integer,Color> colorMap =new HashMap<Integer,Color>();

     for (int i = 0; i < c.getMapSize() ; i++)

     {

           colorMap.put(i, new Color(c.getRGB(i)));

     }

     map = colorMap;

     //we read the raster from the bufferdImage

     Raster r = img.getData();

     //we make a matrix with the same Dimension than the raster               

     matrice = new int[r.getWidth()][r.getHeight()];

    

//for each cells of the matrix, we associate the value of the pixel

// here we know befor which data we were going to read, but it may // be recorded in another format

     for (int i = 0 ; i < matrice.length; i++)

     {

           for (int j = 0 ; j < matrice[0].length; j++)

           {

           Object tab = null;

           Object o = r.getDataElements(i,j,tab);

           r.getSampleModel().getDataType();

           if ( o != null && o instanceof byte[])

           {

           byte[] tab_byte = (byte[])o;

matrice[i][matrice[0].length -j -1] = (int)tab_byte[0];

           }

           }

     }

} catch (IOException e) {

     e.printStackTrace();

}

return matrice;

}

G        Lors de la création, on lit l'image du coin supérieur gauche au coin inférieur droit. On doit donc adapter le parcours pour construire un tableau cohérent (puisque l'on parcourt les tableaux du coin inférieur gauche vers le coin supérieur droit).

Création du sol

Maintenant que nous avons récupéré nos données, nous pouvons compléter notre classe Create_Ground:

public Context create_ground(Context context,String url,String geographyname )

      {      //we load the grid in an array of integer
     
int[][] matrice = img_raster_loader(url);
      //we create a matrix which will contains the Field_Agent

matrice_terrain
= new Field_Agent[matrice.length][matrice[0].length];
      Dimension dim =
new Dimension(matrice.length,matrice[0].length);
     
//we create the gridValueLayer
     
grille = new GridValueLayer("ScalarField", true,
             
new repast.simphony.space.grid.WrapAroundBorders(),
                                                     dim.
width, dim.height);
      context.addValueLayer(
grille);
ContinuousSpaceFactory continousfactory = ContinuousSpaceFactoryFinder.createContinuousSpaceFactory(
new HashMap());
space
= continousfactory.createContinuousSpace("space", context, new SimpleCartesianAdder(),new WrapAroundBorders(),grille.getDimensions().getWidth(),grille.getDimensions().getHeight());
     
//on parcourt tous les éléments de la grille pour les faire
      //correspondre avec les valeurs enregistrées dans la matrice que l'on a chargé.

     
for (int i = 0; i <grille.getDimensions().getWidth();i++)
            {

for
(int j = 0; j <grille.getDimensions().getHeight();j++)
                  {
                       
grille.set(matrice[i][j], i,j);
                       
//on créé un agent terrain avec la valeur correspondante

matrice_terrain[i][j] = new Field_Agent(matrice[i][j]);
                  }
            }
return context;
}

Redéfinition du style

Nous allons ensuite définir le style permettant de représenter notre « GridValueLayer ».

La plupart des méthodes sont des méthodes de Repast que l'on redéfinit.

§         On utilise le constructeur pour associer le style à notre valueLayer.

§         Puis on charge la table de correspondance que l'on a récupéré lorsque l'on a chargé le fichier pour associer une couleur à chaque valeur.

Public class Style2d_terrain implements ValueLayerStyle
{
      protected ValueLayer layer;
      Map<Integer,Color> colorMap;
      public Style2d_terrain()
      {

      //we get back the colorModel we have read during theinitialisation

            colorMap = Grid_Manager.getmap(); 
      }

      @Override

      public void addValueLayer(ValueLayer layer)
      {
            this.layer = layer;
      }

      @Override

      public int getRed(double... coordinates)
      {
            return colorMap.get((int)layer.get(coordinates)).getRed();
      }

      @Override

      public int getBlue(double... coordinates)
      {
            return colorMap.get((int)layer.get(coordinates)).getBlue();
      }

      @Override

      public int getGreen(double... coordinates)
      {
            Return colorMap.get((int)layer.get(coordinates)).getGreen();
      }

      @Override

      public float getCellSize()
      {
            return cell_size;
      }

      @Override

      public Paint getPaint(double... coordinates)
      {
            return colorMap.get((int)layer.get(coordinates));
      }
}

Configuration de l'affichage

Une fois tous les éléments mis en place, il faut configurer l'affichage:

G        l'affichage voulu est de type 2D.

G        Normalement, si vous lancez la simulation, votre image doit apparaître en arrière plan.


Mémo 15 - Auteur Q.Baduel, adaptation  22.01.13 par jlefur

retour aux mémos     retour au modèle     back to SimMasto home page   retour à la page d'accueil