Code covered by the BSD License  

Highlights from
geotiffwrite

geotiffwrite

by

 

19 Jun 2010 (Updated )

Write a 2D or 3D array to a single or multi-band GeoTIFF file

geotiffwrite(filename, bbox, image, bit_depth, option)
%GEOTIFFWRITE  Write a 2D or 3D array to a single or multi-band GeoTIFF file
%
%  MATLAB's Mapping Toolbox only provides a "geotiffread" function, but
%  it does not have a "geotiffwrite" function (Note). This is the MATLAB 
%  program to write a 2D or 3D array to a single or multi-band GeoTIFF
%  file, where data can be either 1-bit monochrome data (i.e. binary or
%  bilevel), 8-bit unsigned integer, 16-bit signed / unsigned integer, 
%  32-bit signed integer, or 32-bit floating point.
%
%  Note: Starting from version R2011a, MATLAB's Mapping Toolbox also
%        provides its own "geotiffwrite" function
% (http://www.mathworks.com/help/toolbox/map/rn/bsq4us7-1.html#bsu1ro5-1).
%
%  This program is based on GeoTIFF Format Specification under:
%  http://www.remotesensing.org/geotiff/spec/geotiffhome.html
%  or http://download.osgeo.org/geotiff/spec
%
%  It does not need MATLAB's Mapping Toolbox, or any other library.
%
%  The motivation to create such a program is because I want to convert
%  the free satelite data from NASA & USGS to the GARMIN topo map for my
%  Garmin GPS. DEM2TOPO (http://people.uleth.ca/~brad.gom/dem2topo) is a
%  wonderful program that can nicely extract contours from the elevation
%  data. However, only 1201x1201 pixels data are supported. For USGS24K 
%  DEM data, 1-arc-second SRTM1 data, etc, DEM2TOPO cannot process them 
%  directly, which must be converted into GeoTIFF format first (see 
%  "Known Issues" in the above site). Although there is a free Windows 
%  program "3DEM" that can convert USGS24K DEM & SRTM1 data into GeoTIFF
%  format, sometimes you will still need to interpolate those DEM data 
%  for void or missing elevation points.
%
%  If you just want to write DEM data to a GeoTIFF file, all you have to
%  provide is a bounding box (2 latitude & 2 longitude), and a 2D or 3D
%  array. Otherwise, you can provide proper GeoTIFF fields to the option
%  argument. To make your life easier, a GUI program "make_option.m"
%  is also provided. So you can select the proper GeoTIFF fields to
%  generate the option argument.
%
%  I recommend that you use "make_option" program to generate option
%  argument instead of constructing it manually.
%
%  Usage:  [option bbox] = make_option([bbox]);
%          geotiffwrite(filename, bbox, image, [bit_depth, option]);
%
%  filename - GeoTIFF file name to be written.
%
%  bbox - Bounding box with West/East of longitude and South/North of
%         latitude. The latitude & longitude must be in Decimal Degree
%         (DD) format, and they must be in a 2x2 array with the order
%         of:
%
%               [  longitude_West,  latitude_South;
%		   longitude_East,  latitude_North  ]
%
%         Note: If GTModelTypeGeoKey is not ModelTypeGeographic, bbox
%               will be reset to empty.
%
%  image - 2D or 3D array. Although any orientation can be specified
%          through option.Orientation, it is simpler to just use the
%          following default orientation:
%
%            First row at North edge; last row at South edge;
%            First column at West edge; last column at East edge;
%
%          Note: By default, "geotiffwrite" assumes intensity image and
%                option.FullColor is 0. So the third dimension of a 3D
%                image represents different bands. If option.FullColor
%                is 1, the third dimension of 3D image must be 3, which
%                indicates R,G,B. i.e. when the third dimension of a 3D
%                array equals to 3, it represents a 3-band GeoTIFF
%                image, unless the option.FullColor is set to 1. When
%                option.ColorMap is set, the specified palette will be
%                used, and you cannot set option.FullColor to 1 at the
%                same time.
%
%  bit_depth - (optional) Number of bits to represent a data point (i.e. 
%          bits per sample):
%
%            1 for 1-bit monochrome data (i.e. binary or bilevel)
%            8 for 8-bit unsigned integer
%            16 for 16-bit signed integer
%            32 for 32-bit floating point (default)
%            -16 for 16-bit unsigned integer
%            -32 for 32-bit signed integer
%
%          Note: If you set values for option.ColorMap, 16 will be for
%                16-bit unsigned integer, and you cannot write in 32-bit
%                depth or 1-bit depth. If you set option.FullColor to 1,
%                8-bit unsigned integer will be used, and you cannot 
%                change bit_depth for Full Color image.
%
%  option - (optional) A structure of GeoTIFF fields:
%
%          First, please run: [option bbox] = make_option( [bbox] ); This
%          will include most fields, except the following:
%
%          option.NaN
%             By default, NaN value (void data point) in the image
%             variable will be kept untouched. However, if you decide
%             to replace it with other value (e.g. -32768), you can
%             define it here.
%
%          option.FullColor = [0] | 1
%             0: Intensity or ColorMap image; (default)
%             1: FullColor (24-bit RGB) image;
%
%          option.ColorMap                                    [Nx3 array]
%             Where N = 2^bit_depth, and bit_depth can only be 8 or 16.
%             Values in option.ColorMap range from 0 to 65535, and value
%             0 in image points to the first row in the ColorMap. Columns
%             in option.ColorMap are [R G B]. Black is represented by
%             [0 0 0], and white is represented by [65535 65535 65535].
%
%          option.Orientation = [1] | 2 | 3 | 4 | 5 | 6 | 7 | 8
%             1: Row from Top, Col from Left; (default)
%             2: Row from Top, Col from Right;
%             3: Row from Bottom, Col from Right;
%             4: Row from Bottom, Col from Left;
%             5: Row from Left, Col from Top;
%             6: Row from Right, Col from Top;
%             7: Row from Right, Col from Bottom;
%             8: Row from Left, Col from Bottom;
%
%          option.ModelTransformationTag                      [4x4 array]
%             In most case, ModelPixelScaleTag and ModelTiepointTag from
%             "make_option.m" program is sufficient to transform from raster
%             to model space. If raster image requires rotation or shearing,
%             you can use this ModelTransformationTag to define a 4x4 affine
%             transformation matrix.
%
%  Examples:
%  - Although not illustrated, it would be easier to use "make_option.m"
%    for option argument
%
%  Data from UTM projected coordinate systems:
%  ftp://ftp.remotesensing.org/pub/geotiff/samples/spot/chicago/UTM2GTIF.TIF
%  or    http://download.osgeo.org/geotiff/samples/spot/chicago/UTM2GTIF.TIF
%  img = imread('UTM2GTIF.TIF');
%  clear option
%  option.GTModelTypeGeoKey = 1;
%  option.ModelPixelScaleTag = [10;10;0];
%  option.ModelTiepointTag = [0;0;0;444650;4640510;0];
%  option.GeogEllipsoidGeoKey = 7008;
%  option.ProjectedCSTypeGeoKey = 26716;
%  option.GTCitationGeoKey = 'UTM Zone 16N NAD27"';
%  option.GeogCitationGeoKey = 'Clarke, 1866 by Default';
%  geotiffwrite('UTM2GTIFX.TIF',[],img,8,option);
%
%  Data from colormap indexed scan image:
%  ftp://ftp.remotesensing.org/pub/geotiff/samples/usgs/o41078a5.tif
%  or    http://download.osgeo.org/geotiff/samples/usgs/o41078a5.tif
%  [img cmap] = imread('o41078a5.tif');
%  clear option
%  option.GTModelTypeGeoKey = 1;
%  option.ModelPixelScaleTag = [2.4384;2.4384;0];
%  option.ModelTiepointTag = [0;0;0;698753.304798;4556059.506392;0];
%  option.ProjectedCSTypeGeoKey = 32617;
%  option.PCSCitationGeoKey = 'UTM Zone 17 N with WGS84';
%  option.ColorMap = 65535*cmap;
%  geotiffwrite('o41078a5X.tif',[],img,8,option);
%
%  DEM data:
%  ftp://ftp.remotesensing.org/pub/geotiff/samples/usgs/mlatlon.tif
%  or    http://download.osgeo.org/geotiff/samples/usgs/mlatlon.tif
%  img = imread('mlatlon.tif');
%  clear option
%  option.GeographicTypeGeoKey = 4267;
%  option.GTCitationGeoKey = 'Geographic Model/GeoTIFF 1.0';
%  option.GeogCitationGeoKey = 'NAD 27 Datum';
%  geotiffwrite('mlatlonX.tif',[-122.6261,37.4531;-122.0777,38],img,16,option);
%
%  Even simpler:
%  http://dds.cr.usgs.gov/srtm/version2_1/SRTM3/North_America/N46W082.hgt.zip
%  srtm = load_srtm('N46W082.hgt');
%  geotiffwrite('N46W082.tif',[-82,46;-81,47],flipud(srtm.dat));
%
%  - Jimmy Shen (jimmy@rotman-baycrest.on.ca)
%
function geotiffwrite(filename, bbox, image, bit_depth, option)

   %  Set default inputs
   %
   if nargin < 5
      option = [];

      if isempty(bbox)
         help geotiffwrite;
         error('Because you did not specify bounding box, please assign proper values to option argument');
      end
   end

   if nargin < 4
      bit_depth = [];
   end

   if nargin < 3
      error('Usage: geotiffwrite(filename, bbox, image, [bit_depth, option])');
   end

   if isempty(bit_depth)
      bit_depth = 32;
   end

   if isfield(option, 'FullColor') & option.FullColor == 1
      bit_depth = 8;

      if isfield(option, 'ColorMap')
         error('If you set option.FullColor to 1, you cannot set option.ColorMap.');
      end
   else
      option.FullColor = 0;
   end

   if isfield(option, 'ColorMap') & ( abs(bit_depth)==32 | abs(bit_depth)==1 )
      error('If you set values to option.ColorMap, you cannot write in 32-bit depth or 1-bit depth.');
   end

   %  Set void data: GeoTIFF set void data to -32768, while data from
   %  other software can be -32767 or NaN.
   %
%   if strcmpi( class(image), 'double' )
 %     image(find(isnan(image))) = -32768;
  % end

   %  Only for int16 DEM data
   %
%   if bit_depth == 16
 %     image(find(image==-32767)) = -32768;
  % end

   if isfield(option, 'NaN')
      image(find(isnan(image))) = option.NaN;
   end

   %  Set Orientation, 274
   %
   if isfield(option, 'Orientation')
      ifd.Orientation = option.Orientation;

      for i = 1:size(image,3)
         switch ifd.Orientation
         case 2
            image(:,:,i) = fliplr(image(:,:,i));
         case 3
            image(:,:,i) = fliplr(flipud(image(:,:,i)));
         case 4
            image(:,:,i) = flipud(image(:,:,i));
         case 5
            image(:,:,i) = flipud(rot90(image(:,:,i)));
         case 6
            image(:,:,i) = rot90(image(:,:,i));
         case 7
            image(:,:,i) = fliplr(rot90(image(:,:,i)));
         case 8
            image(:,:,i) = fliplr(flipud(rot90(image(:,:,i))));
         end
      end
   else
      % First row at North edge; last row at South edge;
      % First column at West edge; last column at East edge;
      %
      ifd.Orientation = 1;
   end

   if abs(bit_depth) == 1 & mod(size(image,2),8) ~= 0
      error('Please use integral multiple of 8 for 1-bit image size.');
   end


   GeoAsciiParamsTag = '';
   GeoDoubleParamsTag = [];
   GeoAsciiOffset = 0;
   GeoDoubleOffset = 0;
   NumberOfKeys = 0;
   GeoKeyDirectoryTag = [1 1 0 NumberOfKeys];

   %  Set GTModelTypeGeoKey, 1024
   %
   NumberOfKeys = NumberOfKeys + 1;
   GeoKeyDirectoryTag(1, 4) = NumberOfKeys;

   if isfield(option, 'GTModelTypeGeoKey') & option.GTModelTypeGeoKey ~= 2
      code = option.GTModelTypeGeoKey;
      bbox = [];
      disp('Warning: Since your Model Type is not Geographic, bbox is reset to empty.');
   else
      code = 2;					% ModelTypeGeographic
   end

   GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [1024 0 1 code]];

   %  Set GTRasterTypeGeoKey, 1025
   %
   NumberOfKeys = NumberOfKeys + 1;
   GeoKeyDirectoryTag(1, 4) = NumberOfKeys;

   if isfield(option, 'GTRasterTypeGeoKey')
      code = option.GTRasterTypeGeoKey;
   else
      code = 1;					% RasterPixelIsArea
   end

   gifd.GTRasterTypeGeoKey = code;
   GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [1025 0 1 code]];

   %  Set GTCitationGeoKey, 1026
   %
   if isfield(option, 'GTCitationGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = [option.GTCitationGeoKey(:); '|']; cnt = length(code);
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [1026 34737 cnt GeoAsciiOffset]];
      GeoAsciiParamsTag = [GeoAsciiParamsTag; code];
      GeoAsciiOffset = GeoAsciiOffset + cnt;
   end

   %  Set GeographicTypeGeoKey, 2048
   %
   if isfield(option, 'GeographicTypeGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.GeographicTypeGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [2048 0 1 code]];
   elseif ~isempty(bbox)
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
%      code = 4267;				% GCS_NAD27
%      code = 4322;				% GCS_WGS_72
      code = 4326;				% GCS_WGS_84
%      code = 4269;				% GCS_NAD83
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [2048 0 1 code]];
   end

   %  Set GeogCitationGeoKey, 2049
   %
   if isfield(option, 'GeogCitationGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = [option.GeogCitationGeoKey(:); '|']; cnt = length(code);
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [2049 34737 cnt GeoAsciiOffset]];
      GeoAsciiParamsTag = [GeoAsciiParamsTag; code];
      GeoAsciiOffset = GeoAsciiOffset + cnt;
   end

   %  Set GeogGeodeticDatumGeoKey, 2050
   %
   if isfield(option, 'GeogGeodeticDatumGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.GeogGeodeticDatumGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [2050 0 1 code]];
   end

   %  Set GeogPrimeMeridianGeoKey, 2051
   %
   if isfield(option, 'GeogPrimeMeridianGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.GeogPrimeMeridianGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [2051 0 1 code]];
   end

   %  Set GeogLinearUnitsGeoKey, 2052
   %
   if isfield(option, 'GeogLinearUnitsGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.GeogLinearUnitsGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [2052 0 1 code]];
   end

   %  Set GeogLinearUnitSizeGeoKey, 2053
   %
   if isfield(option, 'GeogLinearUnitSizeGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.GeogLinearUnitSizeGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [2053 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set GeogAngularUnitsGeoKey, 2054
   %
   if isfield(option, 'GeogAngularUnitsGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.GeogAngularUnitsGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [2054 0 1 code]];
   end

   %  Set GeogAngularUnitSizeGeoKey, 2055
   %
   if isfield(option, 'GeogAngularUnitSizeGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.GeogAngularUnitSizeGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [2055 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set GeogEllipsoidGeoKey, 2056
   %
   if isfield(option, 'GeogEllipsoidGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.GeogEllipsoidGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [2056 0 1 code]];
   end

   %  Set GeogSemiMajorAxisGeoKey, 2057
   %
   if isfield(option, 'GeogSemiMajorAxisGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.GeogSemiMajorAxisGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [2057 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set GeogSemiMinorAxisGeoKey, 2058
   %
   if isfield(option, 'GeogSemiMinorAxisGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.GeogSemiMinorAxisGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [2058 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set GeogInvFlatteningGeoKey, 2059
   %
   if isfield(option, 'GeogInvFlatteningGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.GeogInvFlatteningGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [2059 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set GeogAzimuthUnitsGeoKey, 2060
   %
   if isfield(option, 'GeogAzimuthUnitsGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.GeogAzimuthUnitsGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [2060 0 1 code]];
   end

   %  Set GeogPrimeMeridianLongGeoKey, 2061
   %
   if isfield(option, 'GeogPrimeMeridianLongGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.GeogPrimeMeridianLongGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [2061 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set ProjectedCSTypeGeoKey, 3072
   %
   if isfield(option, 'ProjectedCSTypeGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjectedCSTypeGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3072 0 1 code]];
   end

   %  Set PCSCitationGeoKey, 3073
   %
   if isfield(option, 'PCSCitationGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = [option.PCSCitationGeoKey(:); '|']; cnt = length(code);
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3073 34737 cnt GeoAsciiOffset]];
      GeoAsciiParamsTag = [GeoAsciiParamsTag; code];
      GeoAsciiOffset = GeoAsciiOffset + cnt;
   end

   %  Set ProjectionGeoKey, 3074
   %
   if isfield(option, 'ProjectionGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjectionGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3074 0 1 code]];
   end

   %  Set ProjCoordTransGeoKey, 3075
   %
   if isfield(option, 'ProjCoordTransGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjCoordTransGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3075 0 1 code]];
   end

   %  Set ProjLinearUnitsGeoKey, 3076
   %
   if isfield(option, 'ProjLinearUnitsGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjLinearUnitsGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3076 0 1 code]];
   end

   %  Set ProjLinearUnitSizeGeoKey, 3077
   %
   if isfield(option, 'ProjLinearUnitSizeGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjLinearUnitSizeGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3077 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set ProjStdParallel1GeoKey, 3078
   %
   if isfield(option, 'ProjStdParallel1GeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjStdParallel1GeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3078 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set ProjStdParallel2GeoKey, 3079
   %
   if isfield(option, 'ProjStdParallel2GeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjStdParallel2GeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3079 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set ProjNatOriginLongGeoKey, 3080
   %
   if isfield(option, 'ProjNatOriginLongGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjNatOriginLongGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3080 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set ProjNatOriginLatGeoKey, 3081
   %
   if isfield(option, 'ProjNatOriginLatGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjNatOriginLatGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3081 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set ProjFalseEastingGeoKey, 3082
   %
   if isfield(option, 'ProjFalseEastingGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjFalseEastingGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3082 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set ProjFalseNorthingGeoKey, 3083
   %
   if isfield(option, 'ProjFalseNorthingGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjFalseNorthingGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3083 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set ProjFalseOriginLongGeoKey, 3084
   %
   if isfield(option, 'ProjFalseOriginLongGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjFalseOriginLongGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3084 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set ProjFalseOriginLatGeoKey, 3085
   %
   if isfield(option, 'ProjFalseOriginLatGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjFalseOriginLatGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3085 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set ProjFalseOriginEastingGeoKey, 3086
   %
   if isfield(option, 'ProjFalseOriginEastingGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjFalseOriginEastingGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3086 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set ProjFalseOriginNorthingGeoKey, 3087
   %
   if isfield(option, 'ProjFalseOriginNorthingGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjFalseOriginNorthingGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3087 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set ProjCenterLongGeoKey, 3088
   %
   if isfield(option, 'ProjCenterLongGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjCenterLongGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3088 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set ProjCenterLatGeoKey, 3089
   %
   if isfield(option, 'ProjCenterLatGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjCenterLatGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3089 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set ProjCenterEastingGeoKey, 3090
   %
   if isfield(option, 'ProjCenterEastingGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjCenterEastingGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3090 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set ProjCenterOriginNorthingGeoKey, 3091
   %
   if isfield(option, 'ProjCenterOriginNorthingGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjCenterOriginNorthingGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3091 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set ProjScaleAtNatOriginGeoKey, 3092
   %
   if isfield(option, 'ProjScaleAtNatOriginGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjScaleAtNatOriginGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3092 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set ProjScaleAtCenterGeoKey, 3093
   %
   if isfield(option, 'ProjScaleAtCenterGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjScaleAtCenterGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3093 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set ProjAzimuthAngleGeoKey, 3094
   %
   if isfield(option, 'ProjAzimuthAngleGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjAzimuthAngleGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3094 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set ProjStraightVertPoleLongGeoKey, 3095
   %
   if isfield(option, 'ProjStraightVertPoleLongGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.ProjStraightVertPoleLongGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [3095 34736 1 GeoDoubleOffset]];
      GeoDoubleParamsTag = [GeoDoubleParamsTag; code];
      GeoDoubleOffset = GeoDoubleOffset + 1;
   end

   %  Set VerticalCSTypeGeoKey, 4096
   %
   if isfield(option, 'VerticalCSTypeGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.VerticalCSTypeGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [4096 0 1 code]];
   end

   %  Set VerticalCitationGeoKey, 4097
   %
   if isfield(option, 'VerticalCitationGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = [option.VerticalCitationGeoKey(:); '|']; cnt = length(code);
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [4097 34737 cnt GeoAsciiOffset]];
      GeoAsciiParamsTag = [GeoAsciiParamsTag; code];
      GeoAsciiOffset = GeoAsciiOffset + cnt;
   end

   %  Set VerticalUnitsGeoKey, 4099
   %
   if isfield(option, 'VerticalUnitsGeoKey')
      NumberOfKeys = NumberOfKeys + 1;
      GeoKeyDirectoryTag(1, 4) = NumberOfKeys;
      code = option.VerticalUnitsGeoKey;
      GeoKeyDirectoryTag = [GeoKeyDirectoryTag; [4099 0 1 code]];
   end

   %  Set ifd.ImageWidth, 256
   %
   ifd.ImageWidth = size(image, 2);

   %  Set ifd.ImageLength, 257
   %
   ifd.ImageLength = size(image, 1);

   %  Set ifd.BitsPerSample, 258
   %
   ifd.BitsPerSample = abs(bit_depth)*ones(size(image,3),1);

   %  Set ifd.Compression, 259
   %
   ifd.Compression = 1;				% Uncompressed

   %  Set ifd.PhotometricInterpretation, 262
   %
   if isfield(option, 'ColorMap')
      ifd.PhotometricInterpretation = 3;	% Palette color
   elseif isfield(option, 'FullColor') & option.FullColor == 1
      ifd.PhotometricInterpretation = 2;	% RGB
   else
      ifd.PhotometricInterpretation = 1;	% BlackIsZero
   end

   %  Set ifd.StripOffsets, 273
   %
   ifd.StripOffsets = ones(ifd.ImageLength,1) * ifd.ImageWidth ...
	* abs(bit_depth)/8 * size(image,3);	% Num of bytes in a strip
   ifd.StripOffsets = cumsum(ifd.StripOffsets);
   ifd.StripOffsets(end) = [];
   ifd.StripOffsets = [0; ifd.StripOffsets];
   ifd.StripOffsets = ifd.StripOffsets + 8;	% 8 bytes before 1st strip

   %  Set ifd.SamplesPerPixel, 277
   %
   ifd.SamplesPerPixel = size(image, 3);	% Gray level intensity

   %  Set ifd.RowsPerStrip, 278
   %
   ifd.RowsPerStrip = 1;			% rows per strip

   %  Set ifd.StripByteCounts, 279
   %
   ifd.StripByteCounts = ones(ifd.ImageLength,1) * ifd.ImageWidth ...
	* abs(bit_depth)/8 * size(image,3);	% Num of bytes in a strip

   %  Set ifd.XResolution, 282
   %
   ifd.XResolution = 96;			% 96 dpi

   %  Set ifd.YResolution, 283
   %
   ifd.YResolution = 96;			% 96 dpi

   %  Set ifd.PlanarConfiguration, 284
   %
   ifd.PlanarConfiguration = 1;			% Chunky

   %  Set ifd.ResolutionUnit, 296
   %
   ifd.ResolutionUnit = 2;			% Inch

   %  Set ifd.ColorMap, 320
   %
   if isfield(option, 'ColorMap')
      ifd.ColorMap = option.ColorMap(:);
   end

   %  Set ifd.ExtraSamples, 338
   %
   if ndims(image) > 2
      ifd.ExtraSamples = zeros((size(image,3)-1),1);
   end

   %  Set ifd.SampleFormat, 339
   %
   switch bit_depth
   case {1, -1}
      ifd.SampleFormat = 4*ones(size(image,3),1);	% Undefined
   case {8, -16}
      ifd.SampleFormat = ones(size(image,3),1);		% Unsigned integer
   case {16, -32}
      ifd.SampleFormat = 2*ones(size(image,3),1);	% Signed integer
   case 32
      ifd.SampleFormat = 3*ones(size(image,3),1);	% Floating point
   end

   if isfield(ifd, 'ColorMap') & bit_depth == 16
      ifd.SampleFormat = ones(size(image,3),1);		% Unsigned integer
   end

   if ~isempty(bbox)

      %  Set gifd.ModelPixelScaleTag, 33550
      %
      if gifd.GTRasterTypeGeoKey == 1
         Sx = (bbox(2) - bbox(1)) / ifd.ImageWidth;
         Sy = (bbox(4) - bbox(3)) / ifd.ImageLength;
      elseif gifd.GTRasterTypeGeoKey == 2
         Sx = (bbox(2) - bbox(1)) / (ifd.ImageWidth-1);
         Sy = (bbox(4) - bbox(3)) / (ifd.ImageLength-1);
      else
         error('Incorrect option.GTRasterTypeGeoKey value');
      end

      gifd.ModelPixelScaleTag = [Sx Sy 0];

      %  Set gifd.ModelTiepointTag, 33922
      %
      RasterTiepoint = [0 0 0];
      ModelTiepoint = [bbox(1) bbox(4) 0];
      gifd.ModelTiepointTag = [RasterTiepoint ModelTiepoint];
   else
      if isfield(option, 'ModelPixelScaleTag')
         gifd.ModelPixelScaleTag = option.ModelPixelScaleTag(:);
      else
%         error('ModelPixelScale is required when your Model Type is not Geographic.');
      end

      if isfield(option, 'ModelTiepointTag')
         gifd.ModelTiepointTag = option.ModelTiepointTag(:);
      end

      if isfield(option, 'ModelTransformationTag')
         gifd.ModelTransformationTag = option.ModelTransformationTag(:);
      end

      if ~isfield(option, 'ModelTiepointTag') & ~isfield(option, 'ModelTransformationTag')
         error('Either ModelTiepoint or ModelTransformation is required when your Model Type is not Geographic.');
      end
   end

   %  Write TIFF info
   %
   num_entry = 16;				% 16 basic entries

   if isfield(ifd, 'ColorMap')
      num_entry = num_entry + 1;
   end

   if isfield(ifd, 'ExtraSamples')
      num_entry = num_entry + 1;
   end

   if isfield(gifd, 'ModelPixelScaleTag')
      num_entry = num_entry + 1;
   end

   if isfield(gifd, 'ModelTiepointTag')
      num_entry = num_entry + 1;
   end

   if isfield(gifd, 'ModelTransformationTag')
      num_entry = num_entry + 1;
   end

   if GeoAsciiOffset > 0
      num_entry = num_entry + 1;
   end

   if GeoDoubleOffset > 0
      num_entry = num_entry + 1;
   end

   ifd_end = 8 + sum(ifd.StripByteCounts) + 2 + num_entry*12 + 4;
   fid = fopen(filename, 'wb', 'ieee-le');

   if fid < 0,
      msg = sprintf('Cannot create file "%s".', filename);
      error(msg);
   end

   fwrite(fid, 'II', 'uint8');			% Little-endian
   fwrite(fid, 42, 'uint16');			% TIFF signature
   fwrite(fid, (8 + sum(ifd.StripByteCounts)), 'uint32');	% IFD offset

   %  Write data strip
   %
   image = squeeze(permute(image, [3 2 1]));

   switch abs(bit_depth)
   case 1
      image = bit2byte(image);
      fwrite(fid, image, 'uint8');
   case 8
      fwrite(fid, image, 'uint8');
   case 16
      if isfield(ifd, 'ColorMap') | bit_depth == -16
         fwrite(fid, image, 'uint16');
      else
         fwrite(fid, image, 'int16');
      end
   case 32
      if bit_depth == -32
         fwrite(fid, image, 'int32');
      else
         fwrite(fid, image, 'single');
      end
   end

   fwrite(fid, num_entry, 'uint16');

   %  Entry 1
   %
   fwrite(fid, 256, 'uint16');
   fwrite(fid, 3, 'uint16');		% uint16
   fwrite(fid, 1, 'uint32');		% count
   fwrite(fid, ifd.ImageWidth, 'uint16');
   fwrite(fid, 0, 'uint16');

   %  Entry 2
   %
   fwrite(fid, 257, 'uint16');
   fwrite(fid, 3, 'uint16');		% uint16
   fwrite(fid, 1, 'uint32');		% count
   fwrite(fid, ifd.ImageLength, 'uint16');
   fwrite(fid, 0, 'uint16');

   %  Entry 3
   %
   fwrite(fid, 258, 'uint16');
   fwrite(fid, 3, 'uint16');		% uint16
   fwrite(fid, length(ifd.BitsPerSample), 'uint32');	% count

   if length(ifd.BitsPerSample) > 2
      fwrite(fid, ifd_end, 'uint32');
      ifd_end = ifd_end + length(ifd.BitsPerSample)*2;
   else
      fwrite(fid, ifd.BitsPerSample(1), 'uint16');

      if length(ifd.BitsPerSample) > 1
         fwrite(fid, ifd.BitsPerSample(2), 'uint16');
      else
         fwrite(fid, 0, 'uint16');
      end
   end

   %  Entry 4
   %
   fwrite(fid, 259, 'uint16');
   fwrite(fid, 3, 'uint16');		% uint16
   fwrite(fid, 1, 'uint32');		% count
   fwrite(fid, ifd.Compression, 'uint16');
   fwrite(fid, 0, 'uint16');

   %  Entry 5
   %
   fwrite(fid, 262, 'uint16');
   fwrite(fid, 3, 'uint16');		% uint16
   fwrite(fid, 1, 'uint32');		% count
   fwrite(fid, ifd.PhotometricInterpretation, 'uint16');
   fwrite(fid, 0, 'uint16');

   %  Entry 6
   %
   fwrite(fid, 273, 'uint16');		% StripOffsets
   fwrite(fid, 4, 'uint16');		% uint32
   fwrite(fid, ifd.ImageLength, 'uint32');	% count
   fwrite(fid, ifd_end, 'uint32');
   ifd_end = ifd_end + ifd.ImageLength*4;

   %  Entry 7
   %
   fwrite(fid, 274, 'uint16');
   fwrite(fid, 3, 'uint16');		% uint16
   fwrite(fid, 1, 'uint32');		% count
   fwrite(fid, ifd.Orientation, 'uint16');
   fwrite(fid, 0, 'uint16');

   %  Entry 8
   %
   fwrite(fid, 277, 'uint16');
   fwrite(fid, 3, 'uint16');		% uint16
   fwrite(fid, 1, 'uint32');		% count
   fwrite(fid, ifd.SamplesPerPixel, 'uint16');
   fwrite(fid, 0, 'uint16');

   %  Entry 9
   %
   fwrite(fid, 278, 'uint16');
   fwrite(fid, 3, 'uint16');		% uint16
   fwrite(fid, 1, 'uint32');		% count
   fwrite(fid, ifd.RowsPerStrip, 'uint16');
   fwrite(fid, 0, 'uint16');

   %  Entry 10
   %
   fwrite(fid, 279, 'uint16');		% StripByteCounts
   fwrite(fid, 4, 'uint16');		% uint32
   fwrite(fid, ifd.ImageLength, 'uint32');	% count
   fwrite(fid, ifd_end, 'uint32');
   ifd_end = ifd_end + ifd.ImageLength*4;

   %  Entry 11
   %
   fwrite(fid, 282, 'uint16');		% XResolution
   fwrite(fid, 5, 'uint16');		% uint16
   fwrite(fid, 1, 'uint32');		% count
   fwrite(fid, ifd_end, 'uint32');
   ifd_end = ifd_end + 2*4;

   %  Entry 12
   %
   fwrite(fid, 283, 'uint16');		% YResolution
   fwrite(fid, 5, 'uint16');		% uint16
   fwrite(fid, 1, 'uint32');		% count
   fwrite(fid, ifd_end, 'uint32');
   ifd_end = ifd_end + 2*4;

   %  Entry 13
   %
   fwrite(fid, 284, 'uint16');
   fwrite(fid, 3, 'uint16');		% uint16
   fwrite(fid, 1, 'uint32');		% count
   fwrite(fid, ifd.PlanarConfiguration, 'uint16');
   fwrite(fid, 0, 'uint16');

   %  Entry 14
   %
   fwrite(fid, 296, 'uint16');
   fwrite(fid, 3, 'uint16');		% uint16
   fwrite(fid, 1, 'uint32');		% count
   fwrite(fid, ifd.ResolutionUnit, 'uint16');
   fwrite(fid, 0, 'uint16');

   if isfield(option, 'ColorMap')
      fwrite(fid, 320, 'uint16');
      fwrite(fid, 3, 'uint16');		% uint16
      fwrite(fid, length(ifd.ColorMap), 'uint32');	% count
      fwrite(fid, ifd_end, 'uint32');
      ifd_end = ifd_end + length(ifd.ColorMap)*2;
   end

   if isfield(ifd, 'ExtraSamples')
      fwrite(fid, 338, 'uint16');
      fwrite(fid, 3, 'uint16');		% uint16
      fwrite(fid, length(ifd.ExtraSamples), 'uint32');	% count

      if length(ifd.ExtraSamples) > 2
         fwrite(fid, ifd_end, 'uint32');
         ifd_end = ifd_end + length(ifd.ExtraSamples)*2;
      else
         fwrite(fid, ifd.ExtraSamples(1), 'uint16');

         if length(ifd.ExtraSamples) > 1
            fwrite(fid, ifd.ExtraSamples(2), 'uint16');
         else
            fwrite(fid, 0, 'uint16');
         end
      end
   end

   %  Entry 15
   %
   fwrite(fid, 339, 'uint16');
   fwrite(fid, 3, 'uint16');		% uint16
   fwrite(fid, length(ifd.SampleFormat), 'uint32');	% count

   if length(ifd.SampleFormat) > 2
      fwrite(fid, ifd_end, 'uint32');
      ifd_end = ifd_end + length(ifd.SampleFormat)*2;
   else
      fwrite(fid, ifd.SampleFormat(1), 'uint16');

      if length(ifd.SampleFormat) > 1
         fwrite(fid, ifd.SampleFormat(2), 'uint16');
      else
         fwrite(fid, 0, 'uint16');
      end
   end

   if isfield(gifd, 'ModelPixelScaleTag')
      fwrite(fid, 33550, 'uint16');	% ModelPixelScaleTag
      fwrite(fid, 12, 'uint16');	% double
      fwrite(fid, 3, 'uint32');		% count
      fwrite(fid, ifd_end, 'uint32');
      ifd_end = ifd_end + 3*8;
   end

   if isfield(gifd, 'ModelTiepointTag')
      fwrite(fid, 33922, 'uint16');	% ModelTiepointTag
      fwrite(fid, 12, 'uint16');	% double
      fwrite(fid, length(gifd.ModelTiepointTag), 'uint32');	% count
      fwrite(fid, ifd_end, 'uint32');
      ifd_end = ifd_end + length(gifd.ModelTiepointTag)*8;
   end

   if isfield(gifd, 'ModelTransformationTag')
      fwrite(fid, 34264, 'uint16');	% ModelTransformationTag
      fwrite(fid, 12, 'uint16');	% double
      fwrite(fid, 16, 'uint32');	% count
      fwrite(fid, ifd_end, 'uint32');
      ifd_end = ifd_end + 16*8;
   end

   %  Entry 16
   %
   fwrite(fid, 34735, 'uint16');	% GeoKeyDirectoryTag
   fwrite(fid, 3, 'uint16');		% uint16
   fwrite(fid, prod(size(GeoKeyDirectoryTag)), 'uint32');	% count
   fwrite(fid, ifd_end, 'uint32');
   ifd_end = ifd_end + prod(size(GeoKeyDirectoryTag))*2;

   if GeoDoubleOffset > 0
      fwrite(fid, 34736, 'uint16');	% GeoDoubleParamsTag
      fwrite(fid, 12, 'uint16');	% double
      fwrite(fid, length(GeoDoubleParamsTag), 'uint32');	% count
      fwrite(fid, ifd_end, 'uint32');
      ifd_end = ifd_end + length(GeoDoubleParamsTag)*8;
   end

   if GeoAsciiOffset > 0
      GeoAsciiParamsTag = [GeoAsciiParamsTag; 0];	% TIFF6 requres NUL ending
      fwrite(fid, 34737, 'uint16');	% GeoAsciiParamsTag
      fwrite(fid, 2, 'uint16');		% ascii
      fwrite(fid, length(GeoAsciiParamsTag), 'uint32');		% count
      fwrite(fid, ifd_end, 'uint32');
      ifd_end = ifd_end + length(GeoAsciiParamsTag);
   end

   %  IFD is terminated with 4-byte offset to the next IFD,
   %  or 0 if there are none.
   %
   fwrite(fid, 0, 'uint32');

   if length(ifd.BitsPerSample) > 2
      fwrite(fid, ifd.BitsPerSample, 'uint16');		% 258
   end

   fwrite(fid, ifd.StripOffsets, 'uint32');		% 273
   fwrite(fid, ifd.StripByteCounts, 'uint32');		% 279
   fwrite(fid, ifd.XResolution, 'uint32');		% 282
   fwrite(fid, 1, 'uint32');				% 282
   fwrite(fid, ifd.YResolution, 'uint32');		% 283
   fwrite(fid, 1, 'uint32');				% 283

   if isfield(option, 'ColorMap')
      fwrite(fid, ifd.ColorMap, 'uint16');		% 320
   end

   if isfield(ifd, 'ExtraSamples') & length(ifd.ExtraSamples) > 2
      fwrite(fid, ifd.ExtraSamples, 'uint16');		% 338
   end

   if length(ifd.SampleFormat) > 2
      fwrite(fid, ifd.SampleFormat, 'uint16');		% 339
   end

   if isfield(gifd,'ModelPixelScaleTag') 
      fwrite(fid, gifd.ModelPixelScaleTag, 'double');	% 33550
   end

   if isfield(gifd,'ModelTiepointTag') 
      fwrite(fid, gifd.ModelTiepointTag, 'double');	% 33922
   end

   if isfield(gifd,'ModelTransformationTag')
      fwrite(fid, gifd.ModelTransformationTag, 'double'); % 34264
   end

   GeoKeyDirectoryTag = GeoKeyDirectoryTag';
   fwrite(fid, GeoKeyDirectoryTag, 'uint16');		% 34735

   if GeoDoubleOffset > 0
      fwrite(fid, GeoDoubleParamsTag, 'double');	% 34736
   end

   if GeoAsciiOffset > 0
      fwrite(fid, GeoAsciiParamsTag, 'uint8');		% 34737
   end

   fclose(fid);

   return;					% geotiffwrite


%------------------------------------------------------------------------
%  Convert bits to bytes to store
%
function byte = bit2byte(bit)

%   byte = bit(:);

   %  Pad with 0
   %
%   if mod(length(byte),8) ~= 0
 %     byte = [byte; zeros(8-mod(length(byte),8),1)];
  % end

   byte = reshape(bit, [8, length(bit(:))/8])';
   byte(find(byte>0)) = 1;
   byte(find(byte<1)) = 0;
%   byte = byte*[1 2 4 8 16 32 64 128]';
   byte = double(byte)*[128 64 32 16 8 4 2 1]';

   return;					% bit2byte

Contact us