Adding projection and geotransform metadata to netcdf file

This notebook builds on the ideas that Bryce Nordgren posted to

http://nbviewer.ipython.org/gist/bnordgren/47eee80aae57deb303a0

Basically, we found that Bryce's changes were still not sufficient to produce the gdalinfo metadata we expected to see, nor to geolocate the geotiff file he produced using ArcGIS.

Like Bryce, I am working with this file:

ftp://sidads.colorado.edu/pub/DATASETS/nsidc0531_MEASURES_nhsnow_weekly100/2003/nhtsw100e2_20030107_20030113.nc

N.B. Bryce observes that this file is "supposed to be in EASE-Grid 2.0 format." To begin I would like to clarify that EASE-Grid 2.0 is not a file format. It is a projection definition. Gridded data in EASE-Grid 2.0 can be stored in any file format, including flat binary, HDF, netCDF and geoTIFF. There are three EASE-Grid 2.0 projections, which are all in the equal area family: the Lambert Azimuthal North and South and the global cylindrical equal-area projection (with true latitude at +/- 30 degrees).

Our tests with gdal utilities, ENVI (5.2) and ArcMap (v10) indicate that the following alterations to the original .nc file will enable correct geolocation in these tools. We believe that the files need:

  1. row and column coordinate variables (possibly reversed from what Bryce included, see below for details)
  2. coordinate system spatial reference metadata, and
  3. geotransform metadata

A very interesting source for future reference, on differences between what I saw in epsg wkt and esri/gdal/ogc wkt:

http://gis.stackexchange.com/questions/129764/how-are-esri-wkt-projections-different-from-ogc-wkt-projections

First issue: original file produces this from gdalinfo (on merged_snow_cover_extent): Warning 1: dimension #2 (cols) is not a Longitude/X dimension. Warning 1: dimension #1 (rows) is not a Latitude/Y dimension. and output Coordinate system is undefined, there is no origin/pixel size, and corner points are wrong: Corner Coordinates: Upper Left ( 0.0, 0.0) Lower Left ( 0.0, 180.0) Upper Right ( 180.0, 0.0) Lower Right ( 180.0, 180.0) Center ( 90.0, 90.0)

1) When I added my rows and cols variables, I made them int16, which was too small for the meters range. This eliminated the warnings from gdalinfo, and now the output Coordinate system is defined (correctly, I think) and there are now rows and cols dimension variables, but there is no origin/pixel size and the Corner Coordinates are still Corner Coordinates: Upper Left ( 0.0, 0.0) Lower Left ( 0.0, 180.0) Upper Right ( 180.0, 0.0) Lower Right ( 180.0, 180.0) Center ( 90.0, 90.0) Further, the dimension variables contain junk ( think because python is casting the floats or doubles into int16s): test = d.variables['rows'][::] print test [ 28432 -2640 31824 752 -30320 4144 -26928 7536 -23536 10928 -20144 14320...

2) Next try using a sufficiently large float32 for the dimension variables. This eliminated the warnings from gdalinfo, and now the output Coordinate system is defined (correctly, I think), along with the correct origin/pixel size, and there are now rows and cols dimension variables, the Corner Coordinates are correct:

Upper Left (-9000000.000, 9000000.000) Lower Left (-9000000.000,-9000000.000) Upper Right ( 9000000.000, 9000000.000) Lower Right ( 9000000.000,-9000000.000) Center ( 0.0000000, 0.0000000)

Further, the dimension variables contain what I set (which goes from bottom-up): test = d.variables['rows'][::] print test [-8950000. -8850000. -8750000. -8650000. -8550000. -8450000. -8350000.

However,

gdal_translate -of GTiff -b 1 NETCDF:nhtsw100e2_20030107_20030113.coordvar.nc:merged_snow_cover_extent out.tif complains with this: Input file size is 180, 180 0ERROR 1: nBlockYSize = 180, only 1 supported when reading bottom-up dataset ERROR 1: NETCDF:nhtsw100e2_20030107_20030113.coordvar.nc:merged_snow_cover_extent, band 1: IReadBlock failed at X offset 0, Y offset 0 ERROR 1: GetBlockRef failed at X block offset 0, Y block offset 0

3) So I tried defining the rows to start with the largest numbers first (the UL corner) Now the gdalinfo matches what I got in 2), and there is no error from gdal_translate. Doing gdalinfo on the .nc and .tif files returns different things:

vagrant@vsnowyski:~/ipynb$ diff gdalinfo.coordvar_yUL.nc.txt gdalinfo.coordvar_yUL.tif.txt 1,2c1,2 < Driver: netCDF/Network Common Data Format

< Files: nhtsw100e2_20030107_20030113.coordvar_yUL.nc

Driver: GTiff/GeoTIFF Files: snowcover.coordvar_yUL.tif 10d9 < TOWGS84[0,0,0,0,0,0,0], 12,15c11,12 < PRIMEM["Greenwich",0, < AUTHORITY["EPSG","8901"]], < UNIT["degree",0.0174532925199433,

< AUTHORITY["EPSG","9108"]],

    PRIMEM["Greenwich",0],
    UNIT["degree",0.0174532925199433],

21c18,20

< PARAMETER["false_northing",0]]

PARAMETER["false_northing",0],
UNIT["metre",1,
    AUTHORITY["EPSG","9001"]]]

24a24 AREA_OR_POINT=Area 85,94c85,86 < Geolocation: < LINE_OFFSET=0 < LINE_STEP=1 < PIXEL_OFFSET=0 < PIXEL_STEP=1 < SRS=GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9108"]],AUTHORITY["EPSG","4326"]] < X_BAND=1 < X_DATASET=NETCDF:"nhtsw100e2_20030107_20030113.coordvar_yUL.nc":longitude < Y_BAND=1

< Y_DATASET=NETCDF:"nhtsw100e2_20030107_20030113.coordvar_yUL.nc":latitude

Image Structure Metadata: INTERLEAVE=BAND 101c93

< Band 1 Block=180x180 Type=Byte, ColorInterp=Undefined

Band 1 Block=180x45 Type=Byte, ColorInterp=Gray vagrant@vsnowyski:~/ipynb$ cp snowcover.coordvar_yUL.tif /projects/PMESDR/vagrant/brodzik/gdal_test/

of which the only thing that bothers me is this in the tif:

Band 1 Block=180x45 Type=Byte, ColorInterp=Gray

I would expect it to be 180x180.

However, ArcGIS reads it, fine.


In [1]:
import mpl_toolkits.basemap.pyproj as pyproj
import netCDF4 as nc
import os
import numpy as np
from mpl_toolkits.basemap import Basemap

I open the file for writing, and examine the coordinate system information. I can see the information about the Northern EASE-Grid 2.0, which looks fine.


In [78]:
d.close()


---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-78-ef507f445a30> in <module>()
----> 1 d.close()

/opt/miniconda/lib/python2.7/site-packages/netCDF4.so in netCDF4.Dataset.close (netCDF4.c:23693)()

RuntimeError: NetCDF: Not a valid ID

In [79]:
os.chdir("/home/vagrant/ipynb/")
d = nc.Dataset('nhtsw100e2_20030107_20030113.nc', 'r+')

In [80]:
print d


<type 'netCDF4.Dataset'>
root group (NETCDF4_CLASSIC data model, file format UNDEFINED):
    Conventions: CF-1.6
    Metadata_Conventions: CF-1.6, Unidata Dataset Discovery v1.0, GDS v2.0
    standard_name_vocabulary: CF Standard Name Table (v22, 12 February 2013)
    id: nhtsw100e2_20030107_20030113.nc
    naming_authority: gov.nasa.eosdis
    reference: http://dx.doi.org/10.5067/MEASURES/CRYOSPHERE/nsidc-0531.001
    metadata_link: http://nsidc.org/api/metadata?id=nsidc-0531
    title: MEaSUREs Northern Hemisphere Terrestrial Snow Cover Extent Weekly 100km EASE-Grid 2.0
    product_version: v01r00
    summary: This NASA MEaSUREs Earth System Data Record (ESDR) merges daily Northern Hemisphere snow cover extents over land derived from two independently produced sources.  Variables include snow cover extent from the weekly NOAA/NCDC Northern Hemisphere Snow Cover Extent Climate Data Record (NH SCE CDR) and a gap-filled snow extent product derived from the Special Sensor Microwave/Imager (SSMI) and Special Sensor Microwave Imager/Sounder (SSMIS).  The NSIDC Land-Ocean-Coast-Ice (LOCI) mask derived from BU-MODIS land cover data is consistently applied to each variable.  Data are in a Northern Hemisphere equal area projection at 100 km resolution, and are contained in weekly netCDF files spanning from October 4, 1966 to December 31, 2012.
    keywords: EARTH SCIENCE > CRYOSPHERE > SNOW/ICE > SNOW COVER, EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > SNOW/ICE > SNOW COVER
    keywords_vocabulary: NASA Global Change Master Directory (GCMD) Earth Science Keywords, Version 8.0
    platform: NOAA POES (Polar Orbiting Environmental Satellites), DMSP (Defense Meteorological Satellite Program), GOES (Geostationary Operational Environmental Satellite), METEOSAT, GMS (Japan Geostationary Meteorological Satellite), METOP, TERRA > Earth Observing System TERRA (AM-1), AQUA > Earth Observing System AQUA
    sensor: VISSR > Visible and Infrared Spin Scan Radiometer, VAS > VISSR Atmospheric Sounder, MODIS > Moderate-Resolution Imaging Spectroradiometer, AMSU-B > Advanced Microwave Sounding Unit-B, AMSR-E > Advanced Microwave Scanning Radiometer-EOS, SSMI > Special Sensor Microwave/Imager, SSMIS > Special Sensor Microwave Imager/Sounder, VIIRS > Visible-Infrared Imager-Radiometer Suite
    cdm_data_type: Grid
    source: ftp://data.ncdc.noaa.gov/cdr/snowcover/, ftp://sidads.colorado.edu/pub/DATASETS/nsidc0001_polar_stereo_tbs/
    date_created: 2014-09-09T16:19:30Z
    institution: Center for Environmental Prediction, Rutgers University
    geospatial_lat_units: degrees_north
    geospatial_lon_units: degrees_east
    geospatial_lat_min: 0
    geospatial_lat_max: 90
    geospatial_lon_min: -180
    geospatial_lon_max: 180
    spatial_resolution: 100 km
    time_coverage_start: 2003-01-07
    time_coverage_end: 2003-01-13
    license: No restrictions on access or use
    dimensions(sizes): time(1), rows(180), cols(180)
    variables(dimensions): int32 time(time), float32 latitude(rows,cols), float32 longitude(rows,cols), int8 merged_snow_cover_extent(time,rows,cols), int8 weekly_climate_data_record_snow_cover_extent(time,rows,cols), int8 passive_microwave_gap_filled_snow_cover_extent(time,rows,cols), |S1 coord_system()
    groups: 


In [76]:
test = d.variables['rows'][::]
print test


[ 8950000.  8850000.  8750000.  8650000.  8550000.  8450000.  8350000.
  8250000.  8150000.  8050000.  7950000.  7850000.  7750000.  7650000.
  7550000.  7450000.  7350000.  7250000.  7150000.  7050000.  6950000.
  6850000.  6750000.  6650000.  6550000.  6450000.  6350000.  6250000.
  6150000.  6050000.  5950000.  5850000.  5750000.  5650000.  5550000.
  5450000.  5350000.  5250000.  5150000.  5050000.  4950000.  4850000.
  4750000.  4650000.  4550000.  4450000.  4350000.  4250000.  4150000.
  4050000.  3950000.  3850000.  3750000.  3650000.  3550000.  3450000.
  3350000.  3250000.  3150000.  3050000.  2950000.  2850000.  2750000.
  2650000.  2550000.  2450000.  2350000.  2250000.  2150000.  2050000.
  1950000.  1850000.  1750000.  1650000.  1550000.  1450000.  1350000.
  1250000.  1150000.  1050000.   950000.   850000.   750000.   650000.
   550000.   450000.   350000.   250000.   150000.    50000.   -50000.
  -150000.  -250000.  -350000.  -450000.  -550000.  -650000.  -750000.
  -850000.  -950000. -1050000. -1150000. -1250000. -1350000. -1450000.
 -1550000. -1650000. -1750000. -1850000. -1950000. -2050000. -2150000.
 -2250000. -2350000. -2450000. -2550000. -2650000. -2750000. -2850000.
 -2950000. -3050000. -3150000. -3250000. -3350000. -3450000. -3550000.
 -3650000. -3750000. -3850000. -3950000. -4050000. -4150000. -4250000.
 -4350000. -4450000. -4550000. -4650000. -4750000. -4850000. -4950000.
 -5050000. -5150000. -5250000. -5350000. -5450000. -5550000. -5650000.
 -5750000. -5850000. -5950000. -6050000. -6150000. -6250000. -6350000.
 -6450000. -6550000. -6650000. -6750000. -6850000. -6950000. -7050000.
 -7150000. -7250000. -7350000. -7450000. -7550000. -7650000. -7750000.
 -7850000. -7950000. -8050000. -8150000. -8250000. -8350000. -8450000.
 -8550000. -8650000. -8750000. -8850000. -8950000.]

In [81]:
coords = d.variables['coord_system']
print coords


<type 'netCDF4.Variable'>
|S1 coord_system()
    comment: EASE-Grid-2.0 projection definition documention: http://nsidc.org/data/ease/ease_grid2.html, NSIDC mapx grid parameter definition file: EASE2_N100km.gpd, grid_id: EASE2_N100km
    grid_mapping_name: lambert_azimuthal_equal_area
    longitude_of_projection_origin: 0.0
    latitude_of_projection_origin: 90.0
    false_easting: 0.0
    false_northing: 0.0
    scale_factor_at_projection_origin: 25
    semimajor_axis: 6.37814e+06
    semiminor_axis: 6.35675e+06
    inverse_flattening: 298.257
unlimited dimensions: 
current shape = ()
filling on, default _FillValue of  used

I believe was correct in stating that his problem was that row and column coordinate variables were missing. Bryce outlined an empirical method for determining the values needed, but I know a couple of things about this projection, namely that the origin of the project is the North Pole, which is centered at the intersection of the 4 center cells. The file level metadata is telling me that the resolution is 100 km. I believe it is customary in projection mathematics to assume a left-handed Cartesian coordinate system positioned at the origin, with X increasing to the right and Y increasing up.

In order to set the row and column coordinate values to the centers of each cell, I calculate the values for each axis by multiplying each cell index by the map scale (100000. m). The offset of 50000 m positions each value at the center of each cell.


In [82]:
map_scale_x_meters = 100000.
ncols = len(d.dimensions['cols'])
x_meters = ( np.arange(ncols) - (ncols/2) ) * map_scale_x_meters + (map_scale_x_meters/2)
print x_meters
print type(x_meters[0])


[-8950000. -8850000. -8750000. -8650000. -8550000. -8450000. -8350000.
 -8250000. -8150000. -8050000. -7950000. -7850000. -7750000. -7650000.
 -7550000. -7450000. -7350000. -7250000. -7150000. -7050000. -6950000.
 -6850000. -6750000. -6650000. -6550000. -6450000. -6350000. -6250000.
 -6150000. -6050000. -5950000. -5850000. -5750000. -5650000. -5550000.
 -5450000. -5350000. -5250000. -5150000. -5050000. -4950000. -4850000.
 -4750000. -4650000. -4550000. -4450000. -4350000. -4250000. -4150000.
 -4050000. -3950000. -3850000. -3750000. -3650000. -3550000. -3450000.
 -3350000. -3250000. -3150000. -3050000. -2950000. -2850000. -2750000.
 -2650000. -2550000. -2450000. -2350000. -2250000. -2150000. -2050000.
 -1950000. -1850000. -1750000. -1650000. -1550000. -1450000. -1350000.
 -1250000. -1150000. -1050000.  -950000.  -850000.  -750000.  -650000.
  -550000.  -450000.  -350000.  -250000.  -150000.   -50000.    50000.
   150000.   250000.   350000.   450000.   550000.   650000.   750000.
   850000.   950000.  1050000.  1150000.  1250000.  1350000.  1450000.
  1550000.  1650000.  1750000.  1850000.  1950000.  2050000.  2150000.
  2250000.  2350000.  2450000.  2550000.  2650000.  2750000.  2850000.
  2950000.  3050000.  3150000.  3250000.  3350000.  3450000.  3550000.
  3650000.  3750000.  3850000.  3950000.  4050000.  4150000.  4250000.
  4350000.  4450000.  4550000.  4650000.  4750000.  4850000.  4950000.
  5050000.  5150000.  5250000.  5350000.  5450000.  5550000.  5650000.
  5750000.  5850000.  5950000.  6050000.  6150000.  6250000.  6350000.
  6450000.  6550000.  6650000.  6750000.  6850000.  6950000.  7050000.
  7150000.  7250000.  7350000.  7450000.  7550000.  7650000.  7750000.
  7850000.  7950000.  8050000.  8150000.  8250000.  8350000.  8450000.
  8550000.  8650000.  8750000.  8850000.  8950000.]
<type 'numpy.float64'>

I could calculate the y_meters similarly, but I know that this projection is azimuthal and symmetric, so I can just set y_meters values to be the same as x_meters, but it turns out that the order of the values is important.

At first I did the same thing Bryce did, which was to order the x values from left to right and the y values from top to bottom. This would mean:

y_meters = x_meters[::-1]

However as Bryce points out, this requires that the gdal utilities assume a default orientation for the gridded data that is flipped from top to bottom. This would require that gdal_translate be called with "--config GDAL_NETCDF_BOTTOMUP NO".

I experimented with this and found that setting y_meters with the smallest values first means that I can call gdal utilities without this config switch.


In [83]:
x_meters = x_meters.astype(np.float32)
print x_meters
print type(x_meters[0])


[-8950000. -8850000. -8750000. -8650000. -8550000. -8450000. -8350000.
 -8250000. -8150000. -8050000. -7950000. -7850000. -7750000. -7650000.
 -7550000. -7450000. -7350000. -7250000. -7150000. -7050000. -6950000.
 -6850000. -6750000. -6650000. -6550000. -6450000. -6350000. -6250000.
 -6150000. -6050000. -5950000. -5850000. -5750000. -5650000. -5550000.
 -5450000. -5350000. -5250000. -5150000. -5050000. -4950000. -4850000.
 -4750000. -4650000. -4550000. -4450000. -4350000. -4250000. -4150000.
 -4050000. -3950000. -3850000. -3750000. -3650000. -3550000. -3450000.
 -3350000. -3250000. -3150000. -3050000. -2950000. -2850000. -2750000.
 -2650000. -2550000. -2450000. -2350000. -2250000. -2150000. -2050000.
 -1950000. -1850000. -1750000. -1650000. -1550000. -1450000. -1350000.
 -1250000. -1150000. -1050000.  -950000.  -850000.  -750000.  -650000.
  -550000.  -450000.  -350000.  -250000.  -150000.   -50000.    50000.
   150000.   250000.   350000.   450000.   550000.   650000.   750000.
   850000.   950000.  1050000.  1150000.  1250000.  1350000.  1450000.
  1550000.  1650000.  1750000.  1850000.  1950000.  2050000.  2150000.
  2250000.  2350000.  2450000.  2550000.  2650000.  2750000.  2850000.
  2950000.  3050000.  3150000.  3250000.  3350000.  3450000.  3550000.
  3650000.  3750000.  3850000.  3950000.  4050000.  4150000.  4250000.
  4350000.  4450000.  4550000.  4650000.  4750000.  4850000.  4950000.
  5050000.  5150000.  5250000.  5350000.  5450000.  5550000.  5650000.
  5750000.  5850000.  5950000.  6050000.  6150000.  6250000.  6350000.
  6450000.  6550000.  6650000.  6750000.  6850000.  6950000.  7050000.
  7150000.  7250000.  7350000.  7450000.  7550000.  7650000.  7750000.
  7850000.  7950000.  8050000.  8150000.  8250000.  8350000.  8450000.
  8550000.  8650000.  8750000.  8850000.  8950000.]
<type 'numpy.float32'>

In [84]:
# Use this order (smallest y coordinates first)
# if the data arrays are left alone in the file
# doing this means that gdal_translate doesn't have to be called with 
# "--config GDAL_NETCDF_BOTTOMUP NO" to override the gdal drivers
#y_meters = x_meters

# However, I think the correct way to set up these files for
# gdal drivers to interpret them correctly would be to reverse the
# actual data arrays by row, and then use this for the
# y coordinates:
y_meters = x_meters[::-1]

# It's not clear to me whether this is a CF issue or a gdal convention.

Now I just did the same thing Bryce did to define row and column coordinate variables, with the expected standard_names.


In [85]:
# make the rows coordinate variable
# note the variable name is the same as the dimension name
# the standard_name set to 'projection_y_coordinate'
# is needed for gdal to understand the geolocation
rows_var = d.createVariable('rows',np.float32, ('rows',))

# give it the expected attributes
rows_var.standard_name = 'projection_y_coordinate'
rows_var.axis          = 'Y'
rows_var.units         = 'meters'

# write the values to the variable
rows_var[:] = y_meters

In [86]:
# make the cols coordinate variable similarly
cols_var = d.createVariable('cols',np.float32, ('cols',))
# give it the expected attributes
cols_var.standard_name = 'projection_x_coordinate'
cols_var.axis          = 'X'
cols_var.units         = 'meters'

# write the values to the variable
cols_var[:] = x_meters

In [88]:
print d
print d.variables['rows'][::]


<type 'netCDF4.Dataset'>
root group (NETCDF4_CLASSIC data model, file format UNDEFINED):
    Conventions: CF-1.6
    Metadata_Conventions: CF-1.6, Unidata Dataset Discovery v1.0, GDS v2.0
    standard_name_vocabulary: CF Standard Name Table (v22, 12 February 2013)
    id: nhtsw100e2_20030107_20030113.nc
    naming_authority: gov.nasa.eosdis
    reference: http://dx.doi.org/10.5067/MEASURES/CRYOSPHERE/nsidc-0531.001
    metadata_link: http://nsidc.org/api/metadata?id=nsidc-0531
    title: MEaSUREs Northern Hemisphere Terrestrial Snow Cover Extent Weekly 100km EASE-Grid 2.0
    product_version: v01r00
    summary: This NASA MEaSUREs Earth System Data Record (ESDR) merges daily Northern Hemisphere snow cover extents over land derived from two independently produced sources.  Variables include snow cover extent from the weekly NOAA/NCDC Northern Hemisphere Snow Cover Extent Climate Data Record (NH SCE CDR) and a gap-filled snow extent product derived from the Special Sensor Microwave/Imager (SSMI) and Special Sensor Microwave Imager/Sounder (SSMIS).  The NSIDC Land-Ocean-Coast-Ice (LOCI) mask derived from BU-MODIS land cover data is consistently applied to each variable.  Data are in a Northern Hemisphere equal area projection at 100 km resolution, and are contained in weekly netCDF files spanning from October 4, 1966 to December 31, 2012.
    keywords: EARTH SCIENCE > CRYOSPHERE > SNOW/ICE > SNOW COVER, EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > SNOW/ICE > SNOW COVER
    keywords_vocabulary: NASA Global Change Master Directory (GCMD) Earth Science Keywords, Version 8.0
    platform: NOAA POES (Polar Orbiting Environmental Satellites), DMSP (Defense Meteorological Satellite Program), GOES (Geostationary Operational Environmental Satellite), METEOSAT, GMS (Japan Geostationary Meteorological Satellite), METOP, TERRA > Earth Observing System TERRA (AM-1), AQUA > Earth Observing System AQUA
    sensor: VISSR > Visible and Infrared Spin Scan Radiometer, VAS > VISSR Atmospheric Sounder, MODIS > Moderate-Resolution Imaging Spectroradiometer, AMSU-B > Advanced Microwave Sounding Unit-B, AMSR-E > Advanced Microwave Scanning Radiometer-EOS, SSMI > Special Sensor Microwave/Imager, SSMIS > Special Sensor Microwave Imager/Sounder, VIIRS > Visible-Infrared Imager-Radiometer Suite
    cdm_data_type: Grid
    source: ftp://data.ncdc.noaa.gov/cdr/snowcover/, ftp://sidads.colorado.edu/pub/DATASETS/nsidc0001_polar_stereo_tbs/
    date_created: 2014-09-09T16:19:30Z
    institution: Center for Environmental Prediction, Rutgers University
    geospatial_lat_units: degrees_north
    geospatial_lon_units: degrees_east
    geospatial_lat_min: 0
    geospatial_lat_max: 90
    geospatial_lon_min: -180
    geospatial_lon_max: 180
    spatial_resolution: 100 km
    time_coverage_start: 2003-01-07
    time_coverage_end: 2003-01-13
    license: No restrictions on access or use
    dimensions(sizes): time(1), rows(180), cols(180)
    variables(dimensions): int32 time(time), float32 latitude(rows,cols), float32 longitude(rows,cols), int8 merged_snow_cover_extent(time,rows,cols), int8 weekly_climate_data_record_snow_cover_extent(time,rows,cols), int8 passive_microwave_gap_filled_snow_cover_extent(time,rows,cols), |S1 coord_system(), float32 rows(rows), float32 cols(cols)
    groups: 

[ 8950000.  8850000.  8750000.  8650000.  8550000.  8450000.  8350000.
  8250000.  8150000.  8050000.  7950000.  7850000.  7750000.  7650000.
  7550000.  7450000.  7350000.  7250000.  7150000.  7050000.  6950000.
  6850000.  6750000.  6650000.  6550000.  6450000.  6350000.  6250000.
  6150000.  6050000.  5950000.  5850000.  5750000.  5650000.  5550000.
  5450000.  5350000.  5250000.  5150000.  5050000.  4950000.  4850000.
  4750000.  4650000.  4550000.  4450000.  4350000.  4250000.  4150000.
  4050000.  3950000.  3850000.  3750000.  3650000.  3550000.  3450000.
  3350000.  3250000.  3150000.  3050000.  2950000.  2850000.  2750000.
  2650000.  2550000.  2450000.  2350000.  2250000.  2150000.  2050000.
  1950000.  1850000.  1750000.  1650000.  1550000.  1450000.  1350000.
  1250000.  1150000.  1050000.   950000.   850000.   750000.   650000.
   550000.   450000.   350000.   250000.   150000.    50000.   -50000.
  -150000.  -250000.  -350000.  -450000.  -550000.  -650000.  -750000.
  -850000.  -950000. -1050000. -1150000. -1250000. -1350000. -1450000.
 -1550000. -1650000. -1750000. -1850000. -1950000. -2050000. -2150000.
 -2250000. -2350000. -2450000. -2550000. -2650000. -2750000. -2850000.
 -2950000. -3050000. -3150000. -3250000. -3350000. -3450000. -3550000.
 -3650000. -3750000. -3850000. -3950000. -4050000. -4150000. -4250000.
 -4350000. -4450000. -4550000. -4650000. -4750000. -4850000. -4950000.
 -5050000. -5150000. -5250000. -5350000. -5450000. -5550000. -5650000.
 -5750000. -5850000. -5950000. -6050000. -6150000. -6250000. -6350000.
 -6450000. -6550000. -6650000. -6750000. -6850000. -6950000. -7050000.
 -7150000. -7250000. -7350000. -7450000. -7550000. -7650000. -7750000.
 -7850000. -7950000. -8050000. -8150000. -8250000. -8350000. -8450000.
 -8550000. -8650000. -8750000. -8850000. -8950000.]

However, I found that the coordinate variables still didn't return gdalinfo metadata with the correct corner values, so


In [ ]:
# Add spatial reference and geotransform to coordinate system
# I lifted the following wkt from a geotiff that I created using python gdal tools, where
# I specified the projection with the proj.4 string.
#coords.spatial_ref = "PROJCS[\"unnamed\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"Lambert_Azimuthal_Equal_Area\"],PARAMETER[\"latitude_of_center\",90],PARAMETER[\"longitude_of_center\",0],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"Meter\",1]]"
#coords.spatial_ref = \
#    "PROJCS[\"WGS 84 / NSIDC EASE-Grid 2.0 North\"," \
#    + "  GEOGCS[\"WGS 84\"," \
#    + "    DATUM[\"WGS_1984\"," \
#    + "      SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]]," \
#    + "      TOWGS84[0,0,0,0,0,0,0]," \
#    + "      AUTHORITY[\"EPSG\",\"6326\"]]," \
#    + "    PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]]," \
#    + "    UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]]," \
#    + "    AUTHORITY[\"EPSG\",\"4326\"]]," \
#    + "  PROJECTION[\"Lambert_Azimuthal_Equal_Area\"]," \
#    + "    PARAMETER[\"latitude_of_center\",90]," \
#    + "    PARAMETER[\"longitude_of_center\",0]," \
#    + "    PARAMETER[\"false_easting\",0]," \
#    + "    PARAMETER[\"false_northing\",0]," \
#    + "  UNIT[\"Meter\",1]]"
# This is the wkt for epsg 6931, taken directly from the epsg-registry.org site.
#coords.spatial_ref = \
#    "PROJCS[\"WGS 84 / NSIDC EASE-Grid 2.0 North\"," \
#    + "  GEOGCS[\"WGS 84\"," \
#    + "    DATUM[\"WGS_1984\"," \
#    + "      SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]]," \
#    + "      TOWGS84[0,0,0,0,0,0,0]," \
#    + "      AUTHORITY[\"EPSG\",\"6326\"]]," \
#    + "    PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]]," \
#    + "    UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]]]," \
#    + "  PROJECTION[\"Lambert Azimuthal Equal Area\"," \
#    + "    PARAMETER[\"Latitude of natural origin\",90,ANGLEUNIT[\"degree\",0.01745329252]]," \
#    + "    PARAMETER[\"Longitude of natural origin\",0,ANGLEUNIT[\"degree\",0.01745329252]]," \
#    + "    PARAMETER[\"False easting\",0,LENGTHUNIT[\"metre\",1.0]]," \
#    + "    PARAMETER[\"False northing\",0,LENGTHUNIT[\"metre\",1.0]]]," \
#    + "  UNIT[\"Meter\",1]," \
#    + "  AUTHORITY[\"EPSG\",\"6931\"]]"
#coords.GeoTransform = "-9000000 100000 0 9000000 0 -100000 "

In [89]:
#close the file to make sure everything is written
d.close()
print "Done"


Done

In [ ]: