2 Replies Latest reply: Apr 21, 2012 10:56 PM by Maxideon RSS

    Add geo tags to TIFF image

    865020
      Hello ..
      I struck with adding geotags such as GTModelType_TAG,GTRasterType_TAG,Projection_TAG etc.to TIFF image.Can i add tags to IIOMetadata or TIFFDirectory or anyother.Please suggest me ,if possible give code snippet also.

      Is adding geotags is same as adding our own tag to TIFF image.

      Thanks in advance.
        • 1. Re: Add geo tags to TIFF image
          Maxideon
          Try this. This code assumes you hava JAI-ImageIO, and consequently have the ImageReader/Writer plugin for tiff files.
          public static IIOMetadata addGeoMeta(
                  IIOMetadata existingMeta, 
                  int[] geoKeyDirectory,
                  double[] geoDoubleParams,
                  String geoAsciiParams) {
          
              if(geoKeyDirectory.length%4 != 0) {
                  throw new IllegalArgumentException("Keys come in blocks of 4.  The "
                          + "geoKeyDirectory needs to have a multiple length of 4.");
              }
          
              //some declarations from the various specificiations
              final String METADATA_FORMAT = 
                      "com_sun_media_imageio_plugins_tiff_image_1.0";
              final String GEO_TAGSET = 
                      "com.sun.media.imageio.plugins.tiff.GeoTIFFTagSet";
              final String GEO_KEY_TAG_NUMBER = "34735";
              final String GEO_KEY_TAG_NAME = "GeoKeyDirectory";
              final String GEO_DOUBLES_TAG_NUMBER = "34736";
              final String GEO_DOUBLES_TAG_NAME = "GeoDoubleParams";
              final String GEO_ASCII_TAG_NUMBER = "34737";
              final String GEO_ASCII_TAG_NAME = "GeoAsciiParams";
          
          
              IIOMetadataNode root =  
                      (IIOMetadataNode) existingMeta.getAsTree(METADATA_FORMAT);
          
              IIOMetadataNode ifd = 
                  (IIOMetadataNode) root.getElementsByTagName("TIFFIFD").item(0);
          
          
              //remove old geo data if present
              NodeList children = ifd.getElementsByTagName("TIFFField");
              for(int i = 0; i < children.getLength(); i++) {
                  IIOMetadataNode child = (IIOMetadataNode) children.item(i);
          
                  String tagNumber = child.getAttribute("number");
                  if(GEO_KEY_TAG_NUMBER.equals(tagNumber) || 
                     GEO_DOUBLES_TAG_NUMBER.equals(tagNumber) ||
                     GEO_ASCII_TAG_NUMBER.equals(tagNumber)) {
                      ifd.removeChild(child);
                  }
              }
          
              /**Metadata tree structure:
               * 
               * com_sun_media_imageio_plugins_tiff_image_1.0";
               *   TIFFIFD (attributes: tagSets)
               *     TIFFField (attribues: name & number)
               *     TIFFField (attribues: name & number)
               *     ...
               *     TIFFField (attribues: name & number) <-- GeoKeyDirectory
               *       TIFFShorts
               *         TIFFShort (attribute: value) --
               *         TIFFShort (attribute: value)   | Keys have 4 entries each
               *         TIFFShort (attribute: value)   |
               *         TIFFSshor (attribute: value) --
               *         ...
               *     TIFFField (attributes: name & number) <-- GeoDoubleParams
               *       TIFFDoubles
               *         TIFFDouble (attribute: value)
               *         TIFFDouble (attribute: value)
               *         TIFFDouble (attribute: value)
               *         ...
               *     TIFFField (attributes: name & number) <--GeoAsciiParams
               *       TIFFAsciis
               *         TIFFAscii (atrribute: value) <-- single string containing
               *                                          all ASCII valued GeoKeys
               */
          
          
              //construct GeoKeyDirectory
              IIOMetadataNode keyDirectoryTag = new IIOMetadataNode("TIFFField");
              keyDirectoryTag.setAttribute("number",GEO_KEY_TAG_NUMBER);
              keyDirectoryTag.setAttribute("name",GEO_KEY_TAG_NAME);
          
              IIOMetadataNode keys = new IIOMetadataNode("TIFFShorts");
          
              for(int i = 0; i < geoKeyDirectory.length; i++) {
                  if(geoKeyDirectory[i] < 0 || geoKeyDirectory[i] > 65535)
                      throw new IllegalArgumentException("Invalide key value: " + 
                              geoKeyDirectory[i] + " Must be unsigned short.");
          
                  IIOMetadataNode tiffShort = new IIOMetadataNode("TIFFShort");
                  tiffShort.setAttribute("value",""+geoKeyDirectory);
          keys.appendChild(tiffShort);
          }

          keyDirectoryTag.appendChild(keys);
          ifd.appendChild(keyDirectoryTag);

          //construct GeoDoubleParams of needed
          if(geoDoubleParams != null) {
          IIOMetadataNode doubleParamsTag = new IIOMetadataNode("TIFFField");
          doubleParamsTag.setAttribute("number",GEO_DOUBLES_TAG_NUMBER);
          doubleParamsTag.setAttribute("name",GEO_DOUBLES_TAG_NAME);

          IIOMetadataNode doubles = new IIOMetadataNode("TIFFDoubles");

          for(int i = 0; i < geoDoubleParams.length; i++) {
          IIOMetadataNode tiffDouble = new IIOMetadataNode("TIFFDouble");
          tiffDouble.setAttribute("value",""+geoDoubleParams[i]);
          doubles.appendChild(tiffDouble);
          }

          doubleParamsTag.appendChild(doubles);
          ifd.appendChild(doubleParamsTag);
          }

          //construct GeoAsciiParams if needed
          if(geoAsciiParams != null) {
          IIOMetadataNode asciiParamsTag = new IIOMetadataNode("TIFFField");
          asciiParamsTag.setAttribute("number",GEO_ASCII_TAG_NUMBER);
          asciiParamsTag.setAttribute("name",GEO_ASCII_TAG_NAME);

          IIOMetadataNode asciis = new IIOMetadataNode("TIFFAsciis");

          IIOMetadataNode tiffAscii = new IIOMetadataNode("TIFFAscii");
          tiffAscii.setAttribute("value",geoAsciiParams);

          asciis.appendChild(tiffAscii);
          asciiParamsTag.appendChild(asciis);
          ifd.appendChild(asciiParamsTag);
          }

          //append geo tag set to IFD if needed
          String tagSets = ifd.getAttribute("tagSets");
          if(!tagSets.contains(GEO_TAGSET)) {
          tagSets += "," + GEO_TAGSET;
          ifd.setAttribute("tagSets",tagSets);
          }

          try {
          existingMeta.setFromTree(METADATA_FORMAT,root);
          }catch(Exception e) {
          //failed to add new geo data
          e.printStackTrace();
          }
          return existingMeta;
          };


          It may be a little easier using JAI, but I don't know how you would do it off the top of my head. JAI is build more for image processing than metadata manipulation.
          • 2. Re: Add geo tags to TIFF image
            Maxideon
            An example from the GeoTiff specification:
            Example:
              GeoKeyDirectoryTag=(   1,     1, 2,     6,
                                  1024,     0, 1,     2,
                                  1026, 34737,12,     0,
                                  2048,     0, 1, 32767,
                                  2049, 34737,14,    12,
                                  2050,     0, 1,     6,
                                  2051, 34736, 1,     0 )
              GeoDoubleParamsTag(34736)=(1.5)
              GeoAsciiParamsTag(34737)=("Custom File|My Geographic|")
            The first line indicates that this is a Version 1 GeoTIFF GeoKey directory, the keys are Rev. 1.2, and there are 6 Keys defined in this tag.

            The next line indicates that the first Key (ID=1024 = GTModelTypeGeoKey) has the value 2 (Geographic), explicitly placed in the entry list (since TIFFTagLocation=0). The next line indicates that the Key 1026 (the GTCitationGeoKey) is listed in the GeoAsciiParamsTag (34737) array, starting at offset 0 (the first in array), and running for 12 bytes and so has the value "Custom File" (the "|" is converted to a null delimiter at the end). Going further down the list, the Key 2051 (GeogLinearUnitSizeGeoKey) is located in the GeoDoubleParamsTag (34736), at offset 0 and has the value 1.5; the value of key 2049 (GeogCitationGeoKey) is "My Geographic".
            Using the code I posted it would be something like this
            int[] keys = {       1,     1, 2,     6,
                              1024,     0, 1,     2,
                              1026, 34737,12,     0,
                              2048,     0, 1, 32767,
                              2049, 34737,14,    12,
                              2050,     0, 1,     6,
                              2051, 34736, 1,     0};
            double[] doubles = {1.5};
            String   ascii = "Custom File|My Geographic|";
            
            ImageReader reader = ImageIO.getImageReadersByFormatName("tiff").next();
            
            ImageInputStream in = ImageIO.createImageInputStream(...)
            reader.setInput(in, true, false);
                    
            IIOImage image = reader.readAll(0, null);
            addGeoMeta(image.getMetadata(),keys,doubles,ascii)
            
            //write out IIOImage
            So it's a pretty low level solution. But a solution nonetheless.