private SavotParam getParam(SavotParam param) {
   String ref = param.getRef();
   if (ref != null && ref.length() > 0) return (SavotParam) refMem.get(ref);
   String id = param.getId();
   if (id != null && id.length() > 0) {
     // register field
     refMem.put(id, param);
     return param;
   }
   return param;
 }
  /**
   * Retourne le type de région, null si non trouvé
   *
   * @return
   */
  private String getRegionType(SavotTable table) {
    ParamSet params = table.getParams();
    int nbParam = params.getItemCount();
    SavotParam param;
    for (int i = 0; i < nbParam; i++) {
      param = (SavotParam) params.getItemAt(i);
      if (param.getName().equals("Region")
          || param.getUtype().equalsIgnoreCase("dal:footprint.geom.segment.shape"))
        return param.getValue();

      if (param.getUtype().equalsIgnoreCase("app:footprint.render.overlay.string")) return "String";
    }

    return null;
  }
  private Color getColor(SavotResource res) {
    ParamSet params = res.getParams();
    int nbParams = params.getItemCount();

    SavotParam param;
    for (int i = 0; i < nbParams; i++) {
      param = (SavotParam) params.getItemAt(i);
      param = getParam(param);
      if (param != null && param.getName().equalsIgnoreCase("color")) {
        String val = param.getValue();
        int idx = Util.indexInArrayOf(val, Action.COLORNAME);
        if (idx >= 0) return Action.MYCOLORS[idx];
        else return Action.decodeStaticRGB(val);
      }
    }

    return null;
  }
  private SubFootprintBean processTable(SavotTable table) {
    String type = getRegionType(table);

    // si le type n'est pas un des types supportés ...
    if (type == null
        || !(type.equals("Box")
            || type.equals("Polygon")
            || type.equals("Circle")
            || type.equals("Pickle")
            || type.equals("String"))) {
      return null;
    }

    String id = table.getId();
    SubFootprintBean subFpBean = null;

    if (type.equals("Polygon")) {
      FieldSet fields = table.getFields();
      int nbFields = fields.getItemCount();

      int idxRAOffset = -1;
      int idxDEOffset = -1;
      SavotField field;
      String utype;
      // boucle sur les champs
      for (int i = 0; i < nbFields; i++) {
        field = (SavotField) fields.getItemAt(i);
        field = getField(field);
        utype = field.getUtype();
        if (utype.equals("stc:AstroCoordArea/Region/reg:Polygon/Vertex/Position[1]")
            || utype.equalsIgnoreCase("stc:AstroCoordArea.Polygon.Vertex.Position.C1")) {
          idxRAOffset = i;

          continue;
        } else if (utype.equals("stc:AstroCoordArea/Region/reg:Polygon/Vertex/Position[2]")
            || utype.equalsIgnoreCase("stc:AstroCoordArea.Polygon.Vertex.Position.C2")) {
          idxDEOffset = i;

          continue;
        }
      }
      // pas de traitement possible dans un tel cas
      if (idxRAOffset < 0 || idxDEOffset < 0) return null;

      TRSet trs = table.getData().getTableData().getTRs();
      int nbRows = trs.getItemCount();
      double[] raOffset = new double[nbRows];
      double[] deOffset = new double[nbRows];
      SavotTR tr;
      double raOff, deOff;
      raOff = deOff = 0.;
      String tmp;
      TDSet tds;
      counter += nbRows;
      for (int i = 0; i < nbRows; i++) {
        tr = (SavotTR) trs.getItemAt(i);
        tds = tr.getTDs();
        try {

          tmp = tds.getContent(idxRAOffset);
          //					System.out.println(tmp);
          raOff = Double.valueOf(tmp).doubleValue();

          tmp = tds.getContent(idxDEOffset);
          //					System.out.println(tmp+"\n");
          deOff = Double.valueOf(tmp).doubleValue();
        } catch (NumberFormatException e) {
          e.printStackTrace();
        }
        // TODO : vérifier unité et faire conversion en degrés
        raOffset[i] = raOff / 3600.;
        deOffset[i] = deOff / 3600.;
      }

      subFpBean = new SubFootprintBean(raOffset, deOffset, id);
      subFpBean.setInSphericalCoords(sphericalCoordinates);

      return subFpBean;
    }

    // TODO : nouveau format
    else if (type.equals("Box")) {
      ParamSet params = table.getParams();
      int nbParams = params.getItemCount();

      SavotParam ctrRAOffsetParam = null;
      SavotParam ctrDEOffsetParam = null;
      SavotParam sizeRAParam = null;
      SavotParam sizeDEParam = null;
      SavotParam param;
      String utype;
      // boucle sur les params
      for (int i = 0; i < nbParams; i++) {
        param = (SavotParam) params.getItemAt(i);
        param = getParam(param);
        utype = param.getUtype();
        if (utype.equals("stc:AstroCoordArea/Region/reg:Box/Center[1]")) {
          ctrRAOffsetParam = param;

          continue;
        } else if (utype.equals("stc:AstroCoordArea/Region/reg:Box/Center[2]")) {
          ctrDEOffsetParam = param;

          continue;
        } else if (utype.equals("stc:AstroCoordArea/Region/reg:Box/Size[1]")) {
          sizeRAParam = param;

          continue;
        } else if (utype.equals("stc:AstroCoordArea/Region/reg:Box/Size[2]")) {
          sizeDEParam = param;

          continue;
        }
      }
      // pas de traitement possible dans un tel cas
      if (ctrRAOffsetParam == null
          || ctrDEOffsetParam == null
          || sizeRAParam == null
          || sizeDEParam == null) {
        return null;
      }

      double[] raOffset = new double[4];
      double[] deOffset = new double[4];

      counter += 4;

      double ctrRAOffset, ctrDEOffset, sizeRA, sizeDE;
      ctrRAOffset = ctrDEOffset = sizeRA = sizeDE = -1;
      try {
        ctrRAOffset = Double.valueOf(ctrRAOffsetParam.getValue()).doubleValue();
        ctrDEOffset = Double.valueOf(ctrDEOffsetParam.getValue()).doubleValue();
        sizeRA = Double.valueOf(sizeRAParam.getValue()).doubleValue();
        sizeDE = Double.valueOf(sizeDEParam.getValue()).doubleValue();
      } catch (NumberFormatException e) {
        e.printStackTrace();
        return null;
      }

      int signRA, signDE;
      for (int i = 0; i < 4; i++) {
        signRA = (i == 1 || i == 2) ? 1 : -1;
        signDE = (i == 0 || i == 1) ? 1 : -1;

        // TODO : conversion en degrees en tenant compte des unités dans le VOTable!!
        raOffset[i] = (ctrRAOffset + signRA * sizeRA * 0.5) / 3600.;
        deOffset[i] = (ctrDEOffset + signDE * sizeDE * 0.5) / 3600.;
      }

      subFpBean = new SubFootprintBean(raOffset, deOffset, id);
      subFpBean.setInSphericalCoords(sphericalCoordinates);
      return subFpBean;

    } else if (type.equals("Circle")) {
      ParamSet params = table.getParams();
      int nbParams = params.getItemCount();

      SavotParam ctrXOffsetParam = null;
      SavotParam ctrYOffsetParam = null;
      SavotParam radiusParam = null;
      SavotParam param;
      String utype;
      // boucle sur les params
      for (int i = 0; i < nbParams; i++) {
        param = (SavotParam) params.getItemAt(i);
        param = getParam(param);
        utype = param.getUtype();
        //				System.out.println(utype);
        if (utype.equals("stc:AstroCoordArea/Region/reg:Sector/Center[1]")
            || utype.equals("stc:AstroCoordArea.Circle.Center.C1")) {
          ctrXOffsetParam = param;

          continue;
        } else if (utype.equals("stc:AstroCoordArea/Region/reg:Sector/Center[2]")
            || utype.equals("stc:AstroCoordArea.Circle.Center.C2")) {
          ctrYOffsetParam = param;

          continue;
        } else if (utype.equals("stc:AstroCoordArea/Region/reg:Circle/radius")
            || utype.equals("stc:AstroCoordArea.Circle.radius")) {
          radiusParam = param;

          continue;
        }
      }
      // pas de traitement possible dans un tel cas
      if (ctrXOffsetParam == null || ctrYOffsetParam == null || radiusParam == null) {
        return null;
      }

      double ctrXOffset, ctrYOffset, radius;

      try {
        ctrXOffset = Double.valueOf(ctrXOffsetParam.getValue()).doubleValue();
        ctrYOffset = Double.valueOf(ctrYOffsetParam.getValue()).doubleValue();
        radius = Double.valueOf(radiusParam.getValue()).doubleValue();
      } catch (Exception e) {
        e.printStackTrace();
        return null;
      }

      // TODO : conversion selon l'unité indiqué dans les params!!
      ctrXOffset = ctrXOffset / 3600.0;
      ctrYOffset = ctrYOffset / 3600.0;
      radius = radius / 3600.0;

      subFpBean = new SubFootprintBean(ctrXOffset, ctrYOffset, radius, id);
      subFpBean.setInSphericalCoords(sphericalCoordinates);
      return subFpBean;
    } else if (type.equals("Pickle")) {
      ParamSet params = table.getParams();
      int nbParams = params.getItemCount();

      SavotParam ctrXOffsetParam = null;
      SavotParam ctrYOffsetParam = null;
      SavotParam internalRadParam = null;
      SavotParam externalRadParam = null;
      SavotParam startAngleParam = null;
      SavotParam endAngleParam = null;

      SavotParam param;
      String utype;
      // boucle sur les params
      for (int i = 0; i < nbParams; i++) {
        param = (SavotParam) params.getItemAt(i);
        param = getParam(param);
        utype = param.getUtype();
        if (utype.equals("stc:AstroCoordArea/Region/reg:Sector/Center[1]")) {
          ctrXOffsetParam = param;

          continue;
        } else if (utype.equals("stc:AstroCoordArea/Region/reg:Sector/Center[2]")) {
          ctrYOffsetParam = param;

          continue;
        } else if (utype.equals("stc:AstroCoordArea/Region/reg:Sector/angle1")) {
          startAngleParam = param;

          continue;
        } else if (utype.equals("stc:AstroCoordArea/Region/reg:Sector/angle2")) {
          endAngleParam = param;

          continue;
        }
        // petite subtilité, car on a le meme utype pour internalRad et externalRad !!
        else if (utype.equals("stc:AstroCoordArea/Region/reg:Circle/radius")
            && internalRadParam == null) {
          internalRadParam = param;

          continue;
        } else if (utype.equals("stc:AstroCoordArea/Region/reg:Circle/radius")) {
          externalRadParam = param;

          continue;
        }
      }
      // pas de traitement possible dans un tel cas
      if (ctrXOffsetParam == null
          || ctrYOffsetParam == null
          || startAngleParam == null
          || endAngleParam == null
          || internalRadParam == null
          || externalRadParam == null) {
        return null;
      }

      double ctrXOffset, ctrYOffset, startAngle, endAngle, internalRad, externalRad;

      try {
        ctrXOffset = Double.valueOf(ctrXOffsetParam.getValue()).doubleValue();
        ctrYOffset = Double.valueOf(ctrYOffsetParam.getValue()).doubleValue();
        startAngle = Double.valueOf(startAngleParam.getValue()).doubleValue();
        endAngle = Double.valueOf(endAngleParam.getValue()).doubleValue();
        internalRad = Double.valueOf(internalRadParam.getValue()).doubleValue();
        externalRad = Double.valueOf(externalRadParam.getValue()).doubleValue();
      } catch (Exception e) {
        e.printStackTrace();
        return null;
      }

      // petite subtilité pour que le rayon interne soit le plus petit
      double tmp = internalRad;
      if (internalRad > externalRad) {
        internalRad = externalRad;
        externalRad = tmp;
      }

      // TODO : conversion selon l'unité indiqué dans les params!!
      ctrXOffset = ctrXOffset / 3600.0;
      ctrYOffset = ctrYOffset / 3600.0;
      double angle = endAngle - startAngle;
      internalRad = internalRad / 3600.0;
      externalRad = externalRad / 3600.0;

      subFpBean =
          new SubFootprintBean(
              ctrXOffset, ctrYOffset, startAngle, angle, internalRad, externalRad, id);
      subFpBean.setInSphericalCoords(sphericalCoordinates);
      return subFpBean;
    } else if (type.equals("String")) {
      ParamSet params = table.getParams();
      int nbParams = params.getItemCount();

      SavotParam raParam = null;
      SavotParam decParam = null;
      SavotParam contentParam = null;
      SavotParam param;
      String utype;
      // boucle sur les params
      for (int i = 0; i < nbParams; i++) {
        param = (SavotParam) params.getItemAt(i);
        param = getParam(param);
        utype = param.getUtype();
        if (utype.equals("stc:AstroCoord.Position2D.Value2.C1")) {
          raParam = param;
        } else if (utype.equals("stc:AstroCoord.Position2D.Value2.C2")) {
          decParam = param;
        } else if (utype.equals("app:footprint.render.overlay.string.content")) {
          contentParam = param;
        }
      }

      // pas de traitement possible dans un tel cas
      if (raParam == null || decParam == null || contentParam == null) {
        System.err.println("something is missing");
        return null;
      }

      double ra, dec;

      try {
        ra = Double.valueOf(raParam.getValue()).doubleValue();
        dec = Double.valueOf(decParam.getValue()).doubleValue();
      } catch (NumberFormatException nfe) {
        nfe.printStackTrace();
        return null;
      }

      ra = ra / 3600.0;
      dec = dec / 3600.0;

      subFpBean = new SubFootprintBean(ra, dec, "center", contentParam.getValue());
      subFpBean.setInSphericalCoords(sphericalCoordinates);
      return subFpBean;
    }

    return null;
  }
  /**
   * construit le footprint associé à une RESOURCE
   *
   * @param res la RESOURCE "racine", pouvant contenir d'autres RESOURCE
   */
  private void processFovResource(SavotResource res) {
    FootprintBean fpBean = new FootprintBean();

    sphericalCoordinates = false;

    tabIndex = 0;

    SubFootprintBean sub;

    //	    System.out.println("Processing resource "+res);
    refMem = new Hashtable<String, MarkupComment>();
    String id = res.getId();
    // par défaut
    fpBean.setInstrumentName(id);

    // dangereux, car c'est sur cet ID qu'on va référencer un FoV
    //		if( id==null || id.length()==0 ) id = res.getName();

    // traitement des params au niveau RESOURCE
    ParamSet params = res.getParams();
    int nbParam = params.getItemCount();
    SavotParam param;
    for (int i = 0; i < nbParam; i++) {
      param = (SavotParam) params.getItemAt(i);

      if (param.getUtype().trim().equalsIgnoreCase(SPHERICAL_COORDS)) {
        sphericalCoordinates = true;
      }

      // position RA du FoV
      if (param.getUcd().equalsIgnoreCase("pos.eq.ra;meta.main")) {
        fpBean.setRa(param.getValue());
      }

      // position DE du FoV
      else if (param.getUcd().equalsIgnoreCase("pos.eq.dec;meta.main")) {
        fpBean.setDe(param.getValue());
      }

      // position RA du centre de rotation du FoV  (PF jan 09)
      if (param.getUcd().equalsIgnoreCase("pos.eq.ra")) {
        fpBean.setRaRot(param.getValue());
      }

      // position DE du centre de rotation du FoV  (PF jan 09)
      else if (param.getUcd().equalsIgnoreCase("pos.eq.dec")) {
        fpBean.setDeRot(param.getValue());
      }
      // angle position du FoV
      else if (param.getUcd().equalsIgnoreCase("pos.posAng")
          || param.getUtype().equals(SIAPExtBuilder.FOV_POS_ANGLE)) {
        double d;
        try {
          d = Double.valueOf(param.getValue()).doubleValue();
        } catch (NumberFormatException e) {
          continue;
          //				    d = 0.;
        }
        fpBean.setPosAngle(d);
      }

      // caractère "movable" du FoV
      else if (param.getName().equalsIgnoreCase("Movable")) {
        boolean b;
        try {
          b = Boolean.valueOf(param.getValue()).booleanValue();
        } catch (NumberFormatException e) {
          continue;
        }
        fpBean.setMovable(b);
      }

      // caractère "rollable" du FoV
      else if (param.getName().equalsIgnoreCase("Rollable")) {
        boolean b;
        try {
          b = Boolean.valueOf(param.getValue()).booleanValue();
        } catch (NumberFormatException e) {
          continue;
        }
        fpBean.setRollable(b);
      }

      // convention interne à Aladin pour affichage dans JTable
      else if (param.getId().equals("InstrumentDescription")) {
        fpBean.setInstrumentDesc(param.getValue());
      }
      // convention interne à Aladin pour affichage dans JTable
      else if (param.getId().equals("InstrumentName")) {
        fpBean.setInstrumentName(param.getValue());
      }
      // convention interne à Aladin pour affichage dans JTable
      else if (param.getId().equals("TelescopeName")) {
        fpBean.setTelescopeName(param.getValue());
      }
      // convention interne à Aladin pour affichage dans JTable
      else if (param.getId().equals("Origin")) {
        fpBean.setOrigin(param.getValue());
      }
    }

    // traitement de la RESOURCE racine
    sub = processResource(res);
    if (sub != null && sub.getNbOfSubParts() > 0) fpBean.addSubFootprintBean(sub);

    /*
    for( int i=0; i<nbTab; i++ ) {
    	processTable((SavotTable)tables.getItemAt(i), i);
    }
    */

    // traitement des RESOURCEs dans RESOURCE (Tom Donaldson)
    ResourceSet resources = res.getResources();
    for (int i = 0; i < resources.getItemCount(); i++) {
      sub = processResource((SavotResource) resources.getItemAt(i));
      if (sub != null) {
        fpBean.addSubFootprintBean(sub);
        // on garde en mémoire les sous-parties d'un FoV --> on les place pour cela dans un
        // container
        String subfpId = ((SavotResource) resources.getItemAt(i)).getId();
        if (subfpId != null && subfpId.length() > 0) {
          FootprintBean container = new FootprintBean();
          container.addSubFootprintBean(sub);
          container.setDisplayInFovList(false);
          // on évite d'écraser un bean existant par un sub-bean
          if (!hash.contains(subfpId)) {
            hash.put(subfpId, container);
          }
        }
      }
    }

    hash.put(id, fpBean);

    // on ne vérifie plus l'existence d'un bean avec le meme nom, on écrase
    if (footprintHash.get(id) != null) {
      Aladin.trace(
          1, "Footprint with ID " + id + "already exists ...\n Existing definition will be erased");
    }
    Aladin.trace(3, "Footprint : add to footprintHash footprint with key " + id + "**");
    footprintHash.put(id, fpBean);
  }