!! manage input and output of grids
!|author: Giovanni Ravazzani
! license: GPL
!
!### History
!
! current version 1.9 -18th March 2025
!
! | version | date | comment |
! |----------|-------------|----------|
! | 0.1 | 02/Nov/2007 | Original code |
! | 0.2 | 30/Nov/2008 | extract grid of specified date |
! | 0.3 | 10/Dec/2008 | export grid to netcdf dataset |
! | 0.4 | 13/Dec/2008 | Add support to read ESRI GRID |
! | 0.5 | 18/May/2009 | Add support to grid_mapping (makes use of GeoLib) |
! | 0.6 | 03/Jul/2009 | export to file (ESRI GRID) |
! | 0.7 | 07/Apr/2010 | added GetDtGrid |
! | 0.8 | 19/May/2011 | increased decimal digits in header of exported esri grid |
! | 0.9 | 17/Jun/2011 | add support to read netcdf file from WRF |
! | 1.0 | 11/Mar/2012 | added GetFirstDate and GetTimeSteps |
! | 1.1 | 17/Jun/2013 | added support for grid with non regularly spaced coordinates in netcdf dataset |
! | 1.2 | 15/Dec/2016 | GetXYSizesFromFile modified to detect x and y coordinates by units |
! | 1.3 | 18/Feb/2019 | GetXYSizesFromFile modified to detect coordinate convention lat-lon or lon-lat |
! | 1.4 | 20/Apr/2021 | ScanTimeStringAsString to parse reference time in netcdf dataset |
! | 1.5 | 11/Apr/2022 | SyncTime to return lower bounding time step of a given datetime
! | 1.6 | 06/Apr/2023 | comments reformatted to adhere FORD documenter standard |
! | 1.7 | 08/Nov/2024 | increased number of decimal places of header in exported ESRI ASCII grid |
! | 1.8 | 21/Jan/2025 | Easting and Northing used to find x and y coordinate |
! | 1.9 | 18/Mar/2025 | minor bug fixing in reading coordinate reference system in netcdf files |
!
!
!### License
! license: GNU GPL
!
! This file is part of
!
! MOSAICO -- MOdular library for raSter bAsed hydrologIcal appliCatiOn.
!
! Copyright (C) 2011 Giovanni Ravazzani
!
!### Module Description:
! set of fortran routines to manage input and output of grids.
! The module supports different file formats:
! _NetCDF_ raster layer CF 1.0 compliant (Climate and Forecast metadata standard)
! _ESRI ASCII_ grid
! _ESRI BINARY_ grid
!
! The module introduces two new variable types:
! `grid_real` to store floating point data, and
! `grid_integer` to store integer data.
!
! Spatial conventions in netCDF file:
!```
! y
! yDim ^ N
! |
! | W E
! |
! | S
! 1 |---------> x
! 1 xDim
!```
!
!index conventions: GRID(x,y)
!
! Spatial conventions used in GridLib:
!
!```
! 1 jdim
! 1 |---------> j
! | N
! |
! | W E
! |
! | S
! idim v
! i
!```
!
!index conventions: GRID(i,j)
! To initialize a new grid variable, either integer or float, you can:
!
! read grid from NetCdf file passing the standard name:
!```fortran
! TYPE(grid_real) :: new_grid
! CALL NewGrid (new_grid, NetCdfFileName, stdName='air_temperature')
!```
!
! read grid from NetCdf file passing name of variable:
!```fortran
! TYPE(grid_real) :: new_grid
! CALL NewGrid (new_grid, NetCdfFileName, variable = 'T2m')
!```
!
! If NetCdf archive is multitemporal, that is contains more grids,
! one for each time, you can retrieve the grid for a given time:
!```fortran
! TYPE(grid_real) :: new_grid
! TYPE (DateTime) :: ncTime
! ncTime = '2007-03-05T11:00:00+00:00'
! CALL NewGrid (new_grid, NetCdfFileName, stdName='air_temperature', time = ncTime)
!```
!
! Read grid from _ESRI ASCII_ file:
!```fortran
! TYPE(grid_real) :: new_grid
! fileName = 'file.asc'
! CALL NewGrid (new_grid, fileName, ESRI_ASCII)
!```
!
! Read grid from _ESRI BINARY_ file:
!```fortran
! TYPE(grid_real) :: new_grid
! fileName = 'file'
! CALL NewGrid (new_grid, fileName, ESRI_BINARY)
!```
!
! Initialize grid using an existing one as template. The new grid
! is initialized with value 0:
!```fortran
! TYPE(grid_real) :: template
! TYPE(grid_integer) :: new_Grid
! CALL NewGrid (newGrid, template)
!```
! If you want assign an initial value different than 0:
!```fortran
! CALL NewGrid (new_Grid, template, 100)
!```
! The initial value must be of the same type as values stored in grid
!
! To export a grid to a file you can:
!
! write the grid to a netcdf file as variable termed VariableName:
!```fortran
! TYPE(grid_real) :: grid
! ... some statements that assign data to grid
! CALL ExportGrid (grid, NetCdfFileName, VariableName, 'add')
!```
! If the netcdf file is multitemporal, you can add grid to already existing ones in file:
!```fortran
! TYPE(grid_real) :: grid
! ... some statements that assign data to grid
! CALL ExportGrid (grid, NetCdfFileName, VariableName, 'append')
!```
! If NetCdfFileName does not exist, it is created.
!
! Write the grid to _ESRI ASCII_ or _BINARY_ file:
!```fortran
! TYPE(grid_real) :: grid
! ... some statements that assign data to grid
! fileName = 'file.asc'
! CALL ExportGrid (grid, fileName, ESRI_ASCII)
! fileName = 'file'
! CALL ExportGrid (grid, fileName, ESRI_BINARY)
!```
! If a grid is no more used you can free memory:
!```fortran
! TYPE(grid_real) :: grid
! ... some statements that assign data to grid
! CALL GridDestroy (grid)
!```
!
! References:
! NetCDF: http://www.unidata.ucar.edu/software/netcdf/
! CF 1.0: http://cf-pcmdi.llnl.gov/
! UDUNITS: http://www.unidata.ucar.edu/software/udunits/
MODULE GridLib
! Modules used:
USE netcdf , ONLY : &
! Imported Parameters:
NF90_NOWRITE, nf90_noerr, NF90_CLOBBER, &
NF90_FLOAT, NF90_DOUBLE, NF90_GLOBAL, NF90_WRITE, &
NF90_UNLIMITED, NF90_INT, NF90_MAX_VAR_DIMS, &
! Imported Routines:
nf90_open, nf90_close, nf90_inq_dimid, nf90_inq_varid, &
nf90_inquire_dimension, nf90_get_var, nf90_strerror, &
nf90_inquire, nf90_get_att, nf90_inquire_variable, &
nf90_create, nf90_def_dim, nf90_def_var, nf90_put_att, &
nf90_enddef, nf90_put_var, nf90_redef
USE DataTypeSizes ,ONLY: &
! Imported Parameters:
short,long,float,double
USE LogLib, ONLY : &
! Imported Routines:
Catch
USE ErrorCodes, ONLY : &
! Imported parameters:
ncIOError, memAllocError, openFileError, genericIOError, &
unknownOption
USE Chronos, ONLY : &
! Imported type definitions:
DateTime , &
! Imported routines:
UtcNow, ToUtc, DateTimeIsDefault, &
ASSIGNMENT(=), OPERATOR(-), &
OPERATOR(+), OPERATOR(==), &
OPERATOR(>), OPERATOR(<), &
!Imported parameters:
timeDefault, timeString
USE GeoLib, ONLY : &
! Imported type definitions:
CRS, &
! Imported parameters:
GEODETIC, UTM, GAUSS_BOAGA, TM, &
WGS84, ED50, ROME40, &
! Imported routines:
SetCRS, SetTransverseMercatorParameters, SetGeodeticParameters
IMPLICIT NONE
! Global (i.e. public) Declarations:
! Global Type Definitions:
TYPE grid_real
INTEGER (KIND = short) :: jdim !!number of columns
INTEGER (KIND = short) :: idim !!number of rows
CHARACTER (LEN = 300) :: standard_name !! CF 1.0 compliant standard name
CHARACTER (LEN = 300) :: var_name !! name of the variable
CHARACTER (LEN = 300) :: long_name !! long descriptive name
CHARACTER (LEN = 300) :: file_name !! name of the file from which grid was read
CHARACTER (LEN = 30) :: units !!UDUNITS compliant measure units
CHARACTER (LEN = 20) :: varying_mode !!mode to vary: sequence, linear
REAL (KIND = float) :: nodata !!scalar identifying missing value
REAL (KIND = float) :: valid_min !!minimum valid value
REAL (KIND = float) :: valid_max !!maximum valid value
TYPE (DateTime) :: reference_time !!reference time from which calculate current
TYPE (DateTime) :: current_time !!current date and time of the grid in memory
TYPE (DateTime) :: next_time !!time when next update is required
INTEGER (KIND = short) :: time_index !!position of grid in time dimension in netcdf file
CHARACTER (LEN = 7) :: time_unit !!define time unit. Accepted values are:
!!seconds, second, sec, s
!!minutes, minute, min
!!hours, hour, hr, h
!!days, day, d
REAL (KIND = float), ALLOCATABLE :: mat(:,:) !!data variable contained in grid
INTEGER (KIND = short), ALLOCATABLE :: Iraster (:,:) !!used to transform not regular grid to regular raster
INTEGER (KIND = short), ALLOCATABLE :: Jraster (:,:) !!used to transform not regular grid to regular raster
!!georeferencing informations
REAL (KIND = float) :: cellsize
REAL (KIND = float) :: xllcorner
REAL (KIND = float) :: yllcorner
CHARACTER (LEN = 1000) :: esri_pe_string !!used by ArcMap 9.2
TYPE (CRS) :: grid_mapping !!Coordinate reference System
END TYPE grid_real
TYPE grid_integer
INTEGER (KIND = short) :: jdim !!number of columns
INTEGER (KIND = short) :: idim !!number of rows
CHARACTER (LEN = 300) :: standard_name !! CF 1.0 compliant standard name
CHARACTER (LEN = 300) :: var_name !! name of the variable
CHARACTER (LEN = 300) :: long_name !! long descriptive name
CHARACTER (LEN = 300) :: file_name !! name of the file from which grid was read
CHARACTER (LEN = 30) :: units !!UDUNITS compliant measure units
CHARACTER (LEN = 20) :: varying_mode !!mode to vary: steady, sequence, linear
INTEGER (KIND = short) :: nodata !!scalar identifying missing value
INTEGER (KIND = long) :: valid_min !!minimum valid value
INTEGER (KIND = long) :: valid_max !!maximum valid value
TYPE (DateTime) :: reference_time !!reference time from which calculate current
TYPE (DateTime) :: current_time !!current date and time of the grid in memory
TYPE (DateTime) :: next_time !!time when next update is required
INTEGER (KIND = short) :: time_index !!position of grid in time dimension in netcdf file
CHARACTER (LEN = 7) :: time_unit !!define time unit. Accepted values are:
!!seconds, second, sec, s
!!minutes, minute, min
!!hours, hour, hr, h
!!days, day, d
INTEGER (KIND = long), ALLOCATABLE :: mat(:,:)!!data contained in grid
INTEGER (KIND = short), ALLOCATABLE :: Iraster (:,:) !!used to transform not regular grid to regular raster
INTEGER (KIND = short), ALLOCATABLE :: Jraster (:,:) !!used to transform not regular grid to regular raster
!!georeferencing informations
REAL (KIND = float) :: cellsize
REAL (KIND = float) :: xllcorner
REAL (KIND = float) :: yllcorner
CHARACTER (LEN = 1000) :: esri_pe_string !!used by ArcMap 9.2
TYPE (CRS) :: grid_mapping !!Coordinate reference System
END TYPE grid_integer
! Global Parameters:
INTEGER (KIND = short), PARAMETER :: ESRI_ASCII = 1
INTEGER (KIND = short), PARAMETER :: ESRI_BINARY = 2
INTEGER (KIND = short), PARAMETER :: NET_CDF = 3
! Global Scalars:
! Global Arrays:
!Global Procedures:
PUBLIC :: Newgrid
PUBLIC :: ExportGrid
PUBLIC :: GridDestroy
PUBLIC :: SetStandardName
PUBLIC :: SetLongName
PUBLIC :: SetUnits
PUBLIC :: SetVaryingMode
PUBLIC :: SetReferenceTime
PUBLIC :: SetCurrentTime
PUBLIC :: SetEsriPeString
PUBLIC :: GetGridMapping
PUBLIC :: GetDtGrid
PUBLIC :: GetFirstDate
PUBLIC :: GetTimeSteps
! Local (i.e. private) Declarations:
! Local Procedures:
PRIVATE :: ParseTime
PRIVATE :: TimeIndex
PRIVATE :: NewGridFloatFromNetCDF
PRIVATE :: NewGridIntegerFromNetCDF
PRIVATE :: NewGridFloatFromFile
PRIVATE :: NewGridFloatFromESRI_ASCII
PRIVATE :: NewGridFloatFromESRI_BINARY
PRIVATE :: NewGridIntegerFromFile
PRIVATE :: NewGridIntegerFromESRI_ASCII
PRIVATE :: NewGridIntegerFromESRI_BINARY
PRIVATE :: NewGridFloatAsGridFloat
PRIVATE :: NewGridFloatAsGridInteger
PRIVATE :: NewGridIntegerAsGridInteger
PRIVATE :: NewGridIntegerAsGridFloat
PRIVATE :: GridDestroyFloat
PRIVATE :: GridDestroyInteger
PRIVATE :: GetXYSizesFromFile
PRIVATE :: GetGeoreferenceFromNCdataSet
PRIVATE :: ExportGridFloatToNetCDF
PRIVATE :: ExportGridFloatToFile
PRIVATE :: ExportGridFloatToESRI_ASCII
PRIVATE :: ExportGridFloatToESRI_BINARY
PRIVATE :: ExportGridIntegerToFile
PRIVATE :: ExportGridIntegerToESRI_ASCII
PRIVATE :: ExportGridIntegerToESRI_BINARY
PRIVATE :: SetStandardNameFloat
PRIVATE :: SetStandardNameInteger
PRIVATE :: SetLongNameFloat
PRIVATE :: SetLongNameInteger
PRIVATE :: SetUnitsFloat
PRIVATE :: SetUnitsInteger
PRIVATE :: SetVaryingModeFloat
PRIVATE :: SetVaryingModeInteger
PRIVATE :: SetReferenceTimeFloat
PRIVATE :: SetReferenceTimeInteger
PRIVATE :: SetCurrentTimeFloat
PRIVATE :: SetCurrentTimeInteger
PRIVATE :: SetEsriPeStringFloat
PRIVATE :: SetEsriPeStringInteger
PRIVATE :: SwapGridRealForward
PRIVATE :: SwapGridIntegerForward
PRIVATE :: SwapGridRealBack
PRIVATE :: SwapGridIntegerBack
PRIVATE :: NextTime
PRIVATE :: GetTimeStepsFromFile
PRIVATE :: GetTimeStepsFromNCid
PRIVATE :: ScanTimeStringAsString
! Local Type Definitions:
! Local Parameters:
INTEGER (KIND = short), PRIVATE, PARAMETER :: MISSING_DEF_INT = -9999
REAL (KIND = float), PRIVATE, PARAMETER :: MISSING_DEF_REAL = -9999.9
! Local Scalars:
! Local Arrays:
! Operator definitions:
! Define new operators or overload existing ones.
INTERFACE NewGrid
!MODULE PROCEDURE NewGridFloatFromNetCDF
!MODULE PROCEDURE NewGridIntegerFromNetCDF
MODULE PROCEDURE NewGridFloatFromFile
MODULE PROCEDURE NewGridIntegerFromFile
MODULE PROCEDURE NewGridFloatAsGridFloat
MODULE PROCEDURE NewGridFloatAsGridInteger
MODULE PROCEDURE NewGridIntegerAsGridInteger
MODULE PROCEDURE NewGridIntegerAsGridFloat
END INTERFACE
INTERFACE ExportGrid
MODULE PROCEDURE ExportGridFloatToNetCDF
MODULE PROCEDURE ExportGridIntegerToNetCDF
MODULE PROCEDURE ExportGridFloatToFile
MODULE PROCEDURE ExportGridIntegerToFile
END INTERFACE
INTERFACE GridDestroy
MODULE PROCEDURE GridDestroyFloat
MODULE PROCEDURE GridDestroyInteger
END INTERFACE
INTERFACE GetTimeSteps
MODULE PROCEDURE GetTimeStepsFromNCid
MODULE PROCEDURE GetTimeStepsFromFile
END INTERFACE
INTERFACE SetStandardName
MODULE PROCEDURE SetStandardNameFloat
MODULE PROCEDURE SetStandardNameInteger
END INTERFACE
INTERFACE SetLongName
MODULE PROCEDURE SetLongNameFloat
MODULE PROCEDURE SetLongNameInteger
END INTERFACE
INTERFACE SetUnits
MODULE PROCEDURE SetUnitsFloat
MODULE PROCEDURE SetUnitsInteger
END INTERFACE
INTERFACE SetVaryingMode
MODULE PROCEDURE SetVaryingModeFloat
MODULE PROCEDURE SetVaryingModeInteger
END INTERFACE
INTERFACE SetReferenceTime
MODULE PROCEDURE SetReferenceTimeFloat
MODULE PROCEDURE SetReferenceTimeInteger
END INTERFACE
INTERFACE SetCurrentTime
MODULE PROCEDURE SetCurrentTimeFloat
MODULE PROCEDURE SetCurrentTimeInteger
END INTERFACE
INTERFACE SetEsriPeString
MODULE PROCEDURE SetEsriPeStringFloat
MODULE PROCEDURE SetEsriPeStringInteger
END INTERFACE
INTERFACE GetGridMapping
MODULE PROCEDURE GetGridMappingFloat
MODULE PROCEDURE GetGridMappingInteger
END INTERFACE
!=======
CONTAINS
!=======
! Define procedures contained in this module.
!==============================================================================
!| Description:
! create a new raster_real reading data from NetCDF file
! The variable to read can be defined by its current name or
! the standard_name. The dimensions x and y of the variable
! is calculated searching from the dimensions of the couple of
! variables with 'standard_name' equal to 'projection_x_coordinate'
! and 'projection_y_coordinate' for projected reference systems
! or 'longitude' and 'latitude' for geographic reference systems
! or 'grid_longitude' and 'grid_latitude' for rotated pole systems
! If a comprehensible reference systems is not found a geodetic
! reference system is supposed.
! Once the variable is retrieved, offset and scale factor are applied
! and a check on minimum and maximum valid value is performed.
SUBROUTINE NewGridFloatFromNetCDF &
!
(layer, fileName, variable, stdName, time)
USE StringManipulation, ONLY: &
!Imported routines:
StringCompact
IMPLICIT NONE
! Arguments with intent(in):
CHARACTER (LEN = *), INTENT(in) :: fileName !!NetCDF file to be read
CHARACTER (LEN = *), OPTIONAL, INTENT(in) :: variable !!variable to read
CHARACTER (LEN = *), OPTIONAL, INTENT(in) :: stdName !!standard name of
!!the variable to read
TYPE (DateTime), OPTIONAL, INTENT(in) :: time !!time of the grid to read
! Arguments with intent(out):
TYPE (grid_real), INTENT (out) :: layer !!gridreal to return
! Local scalars:
INTEGER (KIND = short) :: ios !!error return code
INTEGER (KIND = short) :: ncStatus !!error code returned by NetCDF routines
INTEGER (KIND = short) :: ncId !!NetCdf Id for the file
INTEGER (KIND = short) :: varId !!variable Id
INTEGER (KIND = short) :: nVars !!number of variables
CHARACTER (LEN = 80) :: attribute
INTEGER (KIND = short) :: nDimsVar !!number of dimensions of a variable
INTEGER (KIND = short) :: i, j !!loop index
CHARACTER (LEN = 100) :: variableName
CHARACTER (LEN = 1) :: shp
REAL (KIND = float) :: scale_factor
REAL (KIND = float) :: offset
INTEGER (KIND = short) :: latlon
! Local arrays:
INTEGER, ALLOCATABLE :: slice (:)
REAL (KIND = float), ALLOCATABLE :: tempGrid (:,:)
!------------end of declaration------------------------------------------------
!------------------------------------------------------------------------------
![1.0] open NetCDF dataset with read-only access
!------------------------------------------------------------------------------
ncStatus = nf90_open (fileName, NF90_NOWRITE, ncId)
IF (ncStatus /= nf90_noerr) THEN
CALL Catch ('error', 'GridLib', &
TRIM (nf90_strerror (ncStatus) )//': ', &
code = ncIOError, argument = fileName )
ENDIF
!------------------------------------------------------------------------------
![2.0] get x and y size and allocate array
!------------------------------------------------------------------------------
CALL GetXYSizesFromFile (ncId, layer % jdim, layer % idim, shape = shp, latlon = latlon)
!allocate grid
ALLOCATE ( layer % mat (layer % idim, layer % jdim), STAT = ios )
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'memory allocation ', &
code = memAllocError,argument = variable )
ENDIF
!allocate temporary grid
IF (latlon == 1) THEN !coordinate order lon-lat
ALLOCATE ( tempGrid (layer % jdim, layer % idim), STAT = ios )
ELSE !coordinate order lat-lon
ALLOCATE ( tempGrid (layer % idim, layer % jdim), STAT = ios )
END IF
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'memory allocation ', &
code = memAllocError,argument = variable )
ENDIF
!------------------------------------------------------------------------------
![3.0] get values
!------------------------------------------------------------------------------
IF ( PRESENT (variable) ) THEN
ncStatus = nf90_inq_varid (ncId, variable, varId)
CALL ncErrorHandler (ncStatus)
ELSE IF (PRESENT (stdName) ) THEN !search variable corresponding to standard name
!inquire dataset to retrieve number of dimensions, variables
!and global attributes
ncStatus = nf90_inquire(ncId, nVariables = nVars )
CALL ncErrorHandler (ncStatus)
DO i = 1, nVars
attribute = ''
ncStatus = nf90_get_att (ncId, varid = i, name = 'standard_name', &
values = attribute)
IF (ncStatus == nf90_noerr) THEN !'standard_name' is found
IF ( StringCompact(attribute(1:LEN_TRIM(attribute))) == stdName(1:LEN_TRIM(stdName)) )THEN
varId = i
END IF
END IF
END DO
END IF
!verify number of dimensions of variable to read
ncstatus = nf90_inquire_variable(ncId, varId, ndims = nDimsVar)
CALL ncErrorHandler (ncStatus)
!If number of dimensions = 3 read info about time
IF (nDimsVar == 3) THEN
ALLOCATE ( slice(3) )
!Read time information
CALL ParseTime (ncId, layer % reference_time, layer % time_unit)
!Define current time slice (if present)
IF (PRESENT (time) ) THEN
slice = (/ 1, 1, 1 /)
slice(3) = TimeIndex (ncId, layer % reference_time, &
layer % time_unit, time)
layer % current_time = time
layer % time_index = slice(3)
ELSE !read the first grid
slice = (/ 1, 1, 1 /)
layer % time_index = 1
!set current time
layer % current_time = TimeByIndex (ncId, layer % reference_time, &
layer % time_unit, layer % time_index)
ENDIF
!define time of the next grid
layer % next_time = NextTime (ncId, layer % reference_time, &
layer % time_unit, layer % time_index)
ELSE
ALLOCATE ( slice(2) )
slice = (/ 1, 1/)
layer % current_time = timeDefault
layer % next_time = timeDefault
END IF
ncStatus = nf90_get_var (ncId, varId, tempGrid , start = slice)
CALL ncErrorHandler (ncStatus)
!transpose temporary matrix to grid specification
CALL SwapGridRealForward (tempGrid, layer % mat, latlon)
!deallocate temporary grid
DEALLOCATE (tempGrid)
!------------------------------------------------------------------------------
![4.0] get attributes
!------------------------------------------------------------------------------
ncStatus = nf90_get_att (ncId, varId, name = 'standard_name', &
values = layer % standard_name)
ncStatus = nf90_get_att (ncId, varId, name = 'long_name', &
values = layer % long_name)
ncStatus = nf90_get_att (ncId, varId, name = 'units', &
values = layer % units)
ncStatus = nf90_get_att (ncId, varId, name = 'varying_mode', &
values = layer % varying_mode)
!If attribute is not defined set to default
IF (ncStatus /= nf90_noerr) layer % varying_mode = 'sequence'
ncStatus = nf90_get_att (ncId, varId, name = '_FillValue', &
values = layer % nodata)
!if _FillValue is not defined search for missing_value
IF (ncStatus /= nf90_noerr) THEN
ncStatus = nf90_get_att (ncId, varId, name = 'missing_value', &
values = layer % nodata)
END IF
!if missing_value is not defined set to default
IF (ncStatus /= nf90_noerr) THEN
layer % nodata = MISSING_DEF_REAL
END IF
ncStatus = nf90_get_att (ncId, varId, name = 'valid_min', &
values = layer % valid_min)
!If attribute is not defined set to default
IF (ncStatus /= nf90_noerr) layer % valid_min = layer % nodata
ncStatus = nf90_get_att (ncId, varId, name = 'valid_max', &
values = layer % valid_max)
!If attribute is not defined set to default
IF (ncStatus /= nf90_noerr) layer % valid_max = layer % nodata
ncStatus = nf90_get_att (ncId, varId, name = 'scale_factor', &
values = scale_factor)
!If attribute is not defined set to default
IF (ncStatus /= nf90_noerr) scale_factor = 1.0
ncStatus = nf90_get_att (ncId, varId, name = 'add_offset', &
values = offset)
!If attribute is not defined set to default
IF (ncStatus /= nf90_noerr) offset = 0.
ncStatus = nf90_get_att (ncId, varId, name = 'esri_pe_string', &
values = layer % esri_pe_string)
!If attribute is not defined set to default
IF (ncStatus /= nf90_noerr) layer % esri_pe_string = ''
!------------------------------------------------------------------------------
![5.0] check values and apply scale factor and offset
!------------------------------------------------------------------------------
!retrieve name of variable
ncstatus = nf90_inquire_variable(ncId, varId, name = variableName)
layer % var_name = variableName
DO i = 1, layer % idim
DO j = 1, layer % jdim
IF ( layer % mat (i,j) /= layer % nodata ) THEN
!apply scale factor
layer % mat (i,j) = layer % mat (i,j) * scale_factor
!add offset
layer % mat (i,j) = layer % mat (i,j) + offset
!check lower bound
IF ( layer % valid_min /= layer % nodata .AND. &
layer % mat (i,j) < layer % valid_min ) THEN
layer % mat (i,j) = layer % valid_min
CALL Catch ('info', 'GridLib', 'corrected value exceeding &
lower bound in variable: ', argument = variableName )
END IF
!check upper bound
IF ( layer % valid_max /= layer % nodata .AND. &
layer % mat (i,j) > layer % valid_max ) THEN
layer % mat (i,j) = layer % valid_max
CALL Catch ('info', 'GridLib', 'corrected value exceeding &
upper bound in variable: ', argument = variableName )
END IF
END IF
END DO
END DO
!------------------------------------------------------------------------------
![6.0] set file name
!------------------------------------------------------------------------------
layer % file_name = fileName
!------------------------------------------------------------------------------
![7.0] read georeferencing informations from netCDF file
! Regularize grid if necessary
!------------------------------------------------------------------------------
IF (shp == 'm') THEN !2 dimensional (matrix) coordinate for not regular grid
IF (.NOT. ALLOCATED (layer % Iraster)) THEN
ALLOCATE (layer % Iraster(layer%idim,layer%jdim))
ALLOCATE (layer % Jraster(layer%idim,layer%jdim))
CALL GetGeoreferenceFromNCdataSet (ncId, varId, layer % cellsize, &
layer % xllcorner, layer % yllcorner, &
layer % grid_mapping, layer % Iraster, layer % Jraster, .TRUE.)
END IF
!create a temporary copy of layer % mat
IF (ALLOCATED (tempGrid) ) THEN
DEALLOCATE (tempGrid)
ALLOCATE (tempGrid(layer%idim,layer%jdim))
ELSE
ALLOCATE (tempGrid(layer%idim,layer%jdim))
END IF
tempGrid = layer % mat
layer % mat = layer % nodata
!fill in the regular grid
DO i = 1, layer%idim
DO j = 1, layer%jdim
IF (layer % Iraster (i,j) /= -9999 .AND. layer % Jraster (i,j) /= -9999) THEN
layer % mat (layer%Iraster(i,j),layer%Jraster(i,j)) = tempGrid (i,j)
ELSE
layer % mat(i,j) = layer % nodata
END IF
END DO
END DO
DEALLOCATE (tempGrid)
ELSE !regular grid stores coordinate in 1dimensional array (vector)
CALL GetGeoreferenceFromNCdataSet (ncId, varId, layer % cellsize, &
layer % xllcorner, layer % yllcorner, &
layer % grid_mapping, layer % Iraster, layer % Iraster, .FALSE.)
END IF
!------------------------------------------------------------------------------
![8.0] close NetCDF dataset
!------------------------------------------------------------------------------
ncStatus = nf90_close (ncid)
CALL ncErrorHandler (ncStatus)
END SUBROUTINE NewGridFloatFromNetCDF
!==============================================================================
!| Description:
! create a new grid_integer reading data from NetCDF file
! The variable to read can be defined by its current name or
! the standard_name. The dimensions x and j of the variable
! is calculated searching from the dimensions of the couple of
! variables with 'standard_name' equal to 'projection_x_coordinate'
! and 'projection_y_coordinate' for projected reference systems
! or 'longitude' and 'latitude' for geographic reference systems
! or 'grid_longitude' and 'grid_latitude' for rotated pole systems
! If a comprehensible reference systems is not found a geodetic
! reference system is supposed.
! Once the variable is retrieved, offset and scale factor are applied
! and a check on minimum and maximum valid value is performed.
SUBROUTINE NewGridIntegerFromNetCDF &
!
(layer, fileName, variable, stdName, time)
USE StringManipulation, ONLY: &
!Imported routines:
StringCompact
IMPLICIT NONE
! Scalar arguments with intent(in):
CHARACTER (LEN = *), INTENT(in) :: fileName !!NetCDF file to read
CHARACTER (LEN = *), OPTIONAL, INTENT(in) :: variable !!variable to read
CHARACTER (LEN = *), OPTIONAL, INTENT(in) :: stdName !!standard name of
!!the variable to read
TYPE (DateTime), OPTIONAL, INTENT(in) :: time !!time of the grid to read
! Arguments with intent(out):
TYPE (grid_integer), INTENT (out) :: layer !!gridreal to return
! Local scalars:
INTEGER (KIND = short) :: ios !!error return code
INTEGER (KIND = short) :: ncStatus !!error code return by NetCDF routines
INTEGER (KIND = short) :: ncId !!NetCdf Id for the file
INTEGER (KIND = short) :: varId !!variable Id
INTEGER (KIND = short) :: nVars !!number of variables
CHARACTER (LEN = 80) :: attribute
INTEGER (KIND = short) :: nDimsVar !!number of dimensions of a variable
INTEGER (KIND = short) :: i, j !!loop index
CHARACTER (LEN = 100) :: variableName
CHARACTER (LEN = 1) :: shp
INTEGER (KIND = long) :: scale_factor
INTEGER (KIND = long) :: offset
! Local arrays:
INTEGER, ALLOCATABLE :: slice (:)
INTEGER (KIND = long), ALLOCATABLE :: tempGrid (:,:)
!------------end of declaration------------------------------------------------
!------------------------------------------------------------------------------
![1.0] open NetCDF dataset with read-only access
!------------------------------------------------------------------------------
ncStatus = nf90_open (fileName, NF90_NOWRITE, ncId)
IF (ncStatus /= nf90_noerr) THEN
CALL Catch ('error', 'GridLib', &
TRIM (nf90_strerror (ncStatus) )//': ', &
code = ncIOError, argument = fileName )
ENDIF
!------------------------------------------------------------------------------
![2.0] get x and y size and allocate array
!------------------------------------------------------------------------------
CALL GetXYSizesFromFile (ncId, layer % jdim, layer % idim, shape = shp)
!allocate grid
ALLOCATE ( layer % mat (layer % idim, layer % jdim), STAT = ios )
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'memory allocation ', &
code = memAllocError,argument = variable )
ENDIF
!allocate temporary grid
ALLOCATE ( tempGrid (layer % jdim, layer % idim), STAT = ios )
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'memory allocation ', &
code = memAllocError,argument = variable )
ENDIF
!------------------------------------------------------------------------------
![3.0] get values
!------------------------------------------------------------------------------
IF ( PRESENT (variable) ) THEN
ncStatus = nf90_inq_varid (ncId, variable, varId)
CALL ncErrorHandler (ncStatus)
ELSE IF (PRESENT (stdName) ) THEN !search variable corresponding to standard name
!inquire dataset to retrieve number of dimensions, variables
!and global attributes
ncStatus = nf90_inquire(ncId, nVariables = nVars )
CALL ncErrorHandler (ncStatus)
DO i = 1, nVars
attribute = ''
ncStatus = nf90_get_att (ncId, varid = i, name = 'standard_name', &
values = attribute)
IF (ncStatus == nf90_noerr) THEN !error is raised if 'standard_name' is not found
IF ( StringCompact(attribute(1:LEN_TRIM(attribute))) == stdName(1:LEN_TRIM(stdName)) )THEN
varId = i
END IF
END IF
END DO
END IF
!verify number of dimensions of variable to read
ncstatus = nf90_inquire_variable(ncId, varId, ndims = nDimsVar)
CALL ncErrorHandler (ncStatus)
!If number of dimensions = 3 read info about time
IF (nDimsVar == 3) THEN
ALLOCATE ( slice(3) )
!Read time informations
CALL ParseTime (ncId, layer % reference_time, layer % time_unit)
!Define current time slice (if present)
IF (PRESENT (time) ) THEN
slice = (/ 1, 1, 1 /)
slice(3) = TimeIndex (ncId, layer % reference_time, &
layer % time_unit, time)
layer % current_time = time
layer % time_index = slice(3)
ELSE
slice = (/ 1, 1, 1 /)
ENDIF
ELSE
ALLOCATE ( slice(2) )
slice = (/ 1, 1/)
END IF
ncStatus = nf90_get_var (ncId, varId, tempGrid , start = slice)
CALL ncErrorHandler (ncStatus)
!transpose temporary matrix to grid specification
CALL SwapGridIntegerForward (tempGrid, layer % mat)
!deallocate temporary grid
DEALLOCATE (tempGrid)
!------------------------------------------------------------------------------
![4.0] get attributes
!------------------------------------------------------------------------------
ncStatus = nf90_get_att (ncId, varId, name = 'standard_name', &
values = layer % standard_name)
ncStatus = nf90_get_att (ncId, varId, name = 'long_name', &
values = layer % long_name)
ncStatus = nf90_get_att (ncId, varId, name = 'units', &
values = layer % units)
ncStatus = nf90_get_att (ncId, varId, name = 'varying_mode', &
values = layer % varying_mode)
!If attribute is not defined set to default
IF (ncStatus /= nf90_noerr) layer % varying_mode = 'sequence'
ncStatus = nf90_get_att (ncId, varId, name = '_FillValue', &
values = layer % nodata)
!if _FillValue is not defined search for missing_value
IF (ncStatus /= nf90_noerr) THEN
ncStatus = nf90_get_att (ncId, varId, name = 'missing_value', &
values = layer % nodata)
END IF
!if missing_value is not defined set to default
IF (ncStatus /= nf90_noerr) THEN
layer % nodata = MISSING_DEF_REAL
END IF
ncStatus = nf90_get_att (ncId, varId, name = 'valid_min', &
values = layer % valid_min)
!If attribute is not defined set to default
IF (ncStatus /= nf90_noerr) layer % valid_min = layer % nodata
ncStatus = nf90_get_att (ncId, varId, name = 'valid_max', &
values = layer % valid_max)
!If attribute is not defined set to default
IF (ncStatus /= nf90_noerr) layer % valid_max = layer % nodata
ncStatus = nf90_get_att (ncId, varId, name = 'scale_factor', &
values = scale_factor)
!If attribute is not defined set to default
IF (ncStatus /= nf90_noerr) scale_factor = 1
ncStatus = nf90_get_att (ncId, varId, name = 'add_offset', &
values = offset)
!If attribute is not defined set to default
IF (ncStatus /= nf90_noerr) offset = 0
ncStatus = nf90_get_att (ncId, varId, name = 'esri_pe_string', &
values = layer % esri_pe_string)
!If attribute is not defined set to default
IF (ncStatus /= nf90_noerr) layer % esri_pe_string = ''
!------------------------------------------------------------------------------
![5.0] check values and apply scale factor and offset
!------------------------------------------------------------------------------
!retrieve name of variable
ncstatus = nf90_inquire_variable(ncId, varId, name = variableName)
layer % var_name = variableName
DO i = 1, layer % jdim
DO j = 1, layer % idim
IF ( layer % mat (i,j) /= layer % nodata ) THEN
!apply scale factor
layer % mat (i,j) = layer % mat (i,j) * scale_factor
!add offset
layer % mat (i,j) = layer % mat (i,j) + offset
!check lower bound
IF ( layer % valid_min /= layer % nodata .AND. &
layer % mat (i,j) < layer % valid_min ) THEN
layer % mat (i,j) = layer % valid_min
CALL Catch ('info', 'GridLib', 'corrected value exceeding &
lower bound in variable: ', argument = variableName )
END IF
!check upper bound
IF ( layer % valid_max /= layer % nodata .AND. &
layer % mat (i,j) > layer % valid_max ) THEN
layer % mat (i,j) = layer % valid_max
CALL Catch ('info', 'GridLib', 'corrected value exceeding &
upper bound in variable: ', argument = variableName )
END IF
END IF
END DO
END DO
!------------------------------------------------------------------------------
![6.0] set file name
!------------------------------------------------------------------------------
layer % file_name = fileName
!------------------------------------------------------------------------------
![7.0] read georeferencing informations from netCDF file
!------------------------------------------------------------------------------
IF (shp == 'm') THEN !2 dimensional (matrix) coordinate for not regular grid
IF (.NOT. ALLOCATED (layer % Iraster)) THEN
ALLOCATE (layer % Iraster(layer%idim,layer%jdim))
ALLOCATE (layer % Jraster(layer%idim,layer%jdim))
CALL GetGeoreferenceFromNCdataSet (ncId, varId, layer % cellsize, &
layer % xllcorner, layer % yllcorner, &
layer % grid_mapping, layer % Iraster, layer % Jraster, .TRUE.)
END IF
!create a temporary copy of layer % mat
IF (ALLOCATED (tempGrid) ) THEN
DEALLOCATE (tempGrid)
ALLOCATE (tempGrid(layer%idim,layer%jdim))
ELSE
ALLOCATE (tempGrid(layer%idim,layer%jdim))
END IF
tempGrid = layer % mat
layer % mat = layer % nodata
!fill in the regular grid
DO i = 1, layer%idim
DO j = 1, layer%jdim
IF (layer % Iraster (i,j) /= -9999 .AND. layer % Jraster (i,j) /= -9999) THEN
layer % mat (layer%Iraster(i,j),layer%Jraster(i,j)) = tempGrid (i,j)
ELSE
layer % mat(i,j) = layer % nodata
END IF
END DO
END DO
DEALLOCATE (tempGrid)
ELSE !regular grid stores coordinate in 1dimensional array (vector)
CALL GetGeoreferenceFromNCdataSet (ncId, varId, layer % cellsize, &
layer % xllcorner, layer % yllcorner, &
layer % grid_mapping, layer % Iraster, layer % Iraster, .FALSE.)
END IF
!------------------------------------------------------------------------------
![8.0] close NetCDF dataset
!------------------------------------------------------------------------------
ncStatus = nf90_close (ncid)
CALL ncErrorHandler (ncStatus)
END SUBROUTINE NewGridIntegerFromNetCDF
!==============================================================================
!| Description:
! read a grid from a file.
! List of supported format:
! ESRI_ASCII: ESRI ASCII GRID
! ESRI_BINARY: ESRI BINARY GRID
! NET_CDF: NetCDF CF compliant
SUBROUTINE NewGridFloatFromFile &
!
(layer, fileName, fileFormat, variable, stdName, time)
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(IN) :: fileName !! file to read
INTEGER (KIND = short), INTENT(IN) :: fileFormat !! format of the file to read
CHARACTER (LEN = *), OPTIONAL, INTENT(in) :: variable !!variable to read
CHARACTER (LEN = *), OPTIONAL, INTENT(in) :: stdName !!standard name of
!!the variable to read
TYPE (DateTime), OPTIONAL, INTENT(in) :: time !!time of the grid to read
!Arguments with intent(out):
TYPE (grid_real), INTENT(OUT) :: layer !!gridreal to return
!Local variables:
!------------end of declaration------------------------------------------------
IF ( fileformat == ESRI_ASCII ) THEN
CALL NewGridFloatFromESRI_ASCII (fileName, layer)
ELSE IF ( fileformat == ESRI_BINARY ) THEN
CALL NewGridFloatFromESRI_BINARY (fileName, layer)
ELSE IF ( fileformat == NET_CDF ) THEN
IF (PRESENT(stdName)) THEN
IF (PRESENT (time)) THEN
CALL NewGridFloatFromNetCDF (layer, fileName, stdName = stdName, time = time)
ELSE
CALL NewGridFloatFromNetCDF (layer, fileName, stdName= stdName)
END IF
ELSE IF (PRESENT(variable)) THEN
IF (PRESENT (time)) THEN
CALL NewGridFloatFromNetCDF (layer, fileName, variable = variable, time = time)
ELSE
CALL NewGridFloatFromNetCDF (layer, fileName, variable = variable)
END IF
END IF
ELSE
CALL Catch ('error', 'GridLib', &
'unknown option in reading file grid: ', &
code = unknownOption, argument = fileName )
END IF
END SUBROUTINE NewGridFloatFromFile
!==============================================================================
!! Description:
!! read a float grid from a ESRI ASCII file.
SUBROUTINE NewGridFloatFromESRI_ASCII &
!
(fileName, layer)
USE Utilities, ONLY: &
!Imported routines:
GetUnit
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(in) :: fileName !! file to read
!Arguments with intent(out)
TYPE (grid_real), INTENT (OUT) :: layer !!returned grid float
!Local variables:
INTEGER (KIND = short) :: fileUnit
INTEGER (KIND = short) :: ios
CHARACTER (LEN = 50) :: dummy
INTEGER (KIND = short) :: i, j
REAL (KIND = double) :: corner
!------------end of declaration------------------------------------------------
!open file
fileUnit = GetUnit ()
OPEN (UNIT = fileUnit, file = fileName, STATUS = 'OLD', IOSTAT = ios)
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error opening file: ', &
code = openFileError, argument = fileName )
END IF
!read number of columns
READ (fileUnit,*,IOSTAT = ios) dummy, layer % jdim
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading columns in file: ', &
code = genericIOError, argument = fileName )
END IF
!read number of rows
READ (fileUnit,*,IOSTAT = ios) dummy, layer % idim
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading rows in file: ', &
code = genericIOError, argument = fileName )
END IF
!read xll corner
READ (fileUnit,*,IOSTAT = ios) dummy, corner
layer % xllcorner = corner
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading xll in file: ', &
code = genericIOError, argument = fileName )
END IF
!read yll corner
READ (fileUnit,*,IOSTAT = ios) dummy, corner
layer % yllcorner = corner
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading yll in file: ', &
code = genericIOError, argument = fileName )
END IF
!read cellsize
READ (fileUnit,*,IOSTAT = ios) dummy, layer % cellsize
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading cellsize in file: ', &
code = genericIOError, argument = fileName )
END IF
!read nodata value
READ (fileUnit,*,IOSTAT = ios) dummy, layer % nodata
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading missing value in file: ', &
code = genericIOError, argument = fileName )
END IF
!allocate grid
ALLOCATE ( layer % mat (layer % idim, layer % jdim), STAT = ios )
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'memory allocation ', &
code = memAllocError,argument = fileName )
ENDIF
!read data
DO i = 1,layer % idim
READ (fileUnit,*) ( layer % mat (i,j) , j = 1,layer % jdim )
END DO
CLOSE (fileUnit)
!Set to default other fields
layer % standard_name = ''
layer % long_name = ''
layer % units = ''
layer % varying_mode = 'sequence'
layer % valid_min = layer % nodata
layer % valid_max = layer % nodata
layer % esri_pe_string = ''
layer % reference_time = timeDefault
layer % current_time = timeDefault
layer % next_time = timeDefault
END SUBROUTINE NewGridFloatFromESRI_ASCII
!==============================================================================
!| Description:
! read a float grid from a ESRI BINARY file.
SUBROUTINE NewGridFloatFromESRI_BINARY &
!
(fileName, layer)
USE Utilities, ONLY: &
!Imported routines:
GetUnit
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(in) :: fileName !! file to read
!Arguments with intent(out)
TYPE (grid_real), INTENT (OUT) :: layer !!returned grid float
!Local variables:
INTEGER (KIND = short) :: fileUnit
INTEGER (KIND = short) :: ios
CHARACTER (LEN = 50) :: dummy
INTEGER (KIND = short) :: recordLength
INTEGER (KIND = short) :: recordNumber
INTEGER (KIND = short) :: i, j
!------------end of declaration------------------------------------------------
!open file
fileUnit = GetUnit ()
OPEN (UNIT = fileUnit, file = TRIM(fileName) // '.hdr', &
STATUS = 'OLD', IOSTAT = ios)
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error opening file: ', &
code = openFileError, argument = TRIM(fileName) // '.hdr' )
END IF
!read number of columns
READ (fileUnit,*,IOSTAT = ios) dummy, layer % jdim
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading columns in file: ', &
code = genericIOError, argument = TRIM(fileName) // '.hdr' )
END IF
!read number of rows
READ (fileUnit,*,IOSTAT = ios) dummy, layer % idim
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading rows in file: ', &
code = genericIOError, argument = TRIM(fileName) // '.hdr' )
END IF
!read xll corner
READ (fileUnit,*,IOSTAT = ios) dummy, layer % xllcorner
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading xll in file: ', &
code = genericIOError, argument = TRIM(fileName) // '.hdr' )
END IF
!read yll corner
READ (fileUnit,*,IOSTAT = ios) dummy, layer % yllcorner
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading yll in file: ', &
code = genericIOError, argument = TRIM(fileName) // '.hdr' )
END IF
!read cellsize
READ (fileUnit,*,IOSTAT = ios) dummy, layer % cellsize
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading cellsize in file: ', &
code = genericIOError, argument = TRIM(fileName) // '.hdr' )
END IF
!read nodata value
READ (fileUnit,*,IOSTAT = ios) dummy, layer % nodata
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading missing value in file: ', &
code = genericIOError, argument = TRIM(fileName) // '.hdr' )
END IF
!allocate grid
ALLOCATE ( layer % mat (layer % idim, layer % jdim), STAT = ios )
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'memory allocation ', &
code = memAllocError,argument = fileName )
ENDIF
CLOSE (fileUnit)
fileUnit = GetUnit ()
INQUIRE (IOLENGTH = recordLength) 100_float
OPEN (UNIT = fileUnit, file = TRIM(fileName) // '.flt', &
FORM = 'UNFORMATTED', ACCESS = 'DIRECT', RECL = recordLength, &
STATUS = 'OLD', IOSTAT = ios)
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error opening file: ', &
code = openFileError, argument = TRIM(fileName) // '.flt' )
END IF
!read data
recordNumber = 0
DO i = 1,layer % idim
DO j = 1, layer % jdim
recordNumber = recordNumber + 1
READ (fileUnit,REC = recordNumber) layer % mat (i,j)
END DO
END DO
CLOSE (fileUnit)
!Set other fields to default
layer % standard_name = ''
layer % long_name = ''
layer % units = ''
layer % varying_mode = 'sequence'
layer % valid_min = layer % nodata
layer % valid_max = layer % nodata
layer % esri_pe_string = ''
layer % reference_time = timeDefault
layer % current_time = timeDefault
layer % next_time = timeDefault
END SUBROUTINE NewGridFloatFromESRI_BINARY
!==============================================================================
!| Description:
! read a grid from a file.
!
! * List of supported format:
! * ESRI_ASCII: ESRI ASCII GRID
! * ESRI_BINARY: ESRI BINARY GRID
!
SUBROUTINE NewGridIntegerFromFile &
!
(layer, fileName, fileFormat, variable, stdName, time)
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(IN) :: fileName !! file to read
INTEGER (KIND = short), INTENT(IN) :: fileFormat !! format of the file to read
CHARACTER (LEN = *), OPTIONAL, INTENT(in) :: variable !!variable to read
CHARACTER (LEN = *), OPTIONAL, INTENT(in) :: stdName !!standard name of
!!the variable to read
TYPE (DateTime), OPTIONAL, INTENT(in) :: time !!time of the grid to read
!Arguments with intent(out):
TYPE (grid_integer), INTENT(OUT) :: layer !!grid to be returned
!------------end of declaration------------------------------------------------
IF ( fileformat == ESRI_ASCII ) THEN
CALL NewGridIntegerFromESRI_ASCII (fileName, layer)
ELSE IF ( fileformat == ESRI_BINARY ) THEN
CALL NewGridIntegerFromESRI_BINARY (fileName, layer)
ELSE IF ( fileformat == NET_CDF ) THEN
IF (PRESENT(stdName)) THEN
IF (PRESENT (time)) THEN
CALL NewGridIntegerFromNetCDF (layer, fileName, stdName = stdName, time = time)
ELSE
CALL NewGridIntegerFromNetCDF (layer, fileName, stdName= stdName)
END IF
ELSE IF (PRESENT(variable)) THEN
IF (PRESENT (time)) THEN
CALL NewGridIntegerFromNetCDF (layer, fileName, variable = variable, time = time)
ELSE
CALL NewGridIntegerFromNetCDF (layer, fileName, variable = variable)
END IF
END IF
ELSE
CALL Catch ('error', 'GridLib', &
'unknown option in reading file grid: ', &
code = unknownOption, argument = fileName )
END IF
END SUBROUTINE NewGridIntegerFromFile
!==============================================================================
!| Description:
! read a integer grid from a ESRI ASCII file.
SUBROUTINE NewGridIntegerFromESRI_ASCII &
!
(fileName, layer)
USE Utilities, ONLY: &
!Imported routines:
GetUnit
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(in) :: fileName !! file to read
!Arguments with intent(out)
TYPE (grid_integer), INTENT (OUT) :: layer !!returned grid float
!Local variables:
INTEGER (KIND = short) :: fileUnit
INTEGER (KIND = short) :: ios
CHARACTER (LEN = 50) :: dummy
INTEGER (KIND = short) :: i, j
!------------end of declaration------------------------------------------------
!open file
fileUnit = GetUnit ()
OPEN (UNIT = fileUnit, file = fileName, STATUS = 'OLD', IOSTAT = ios)
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error opening file: ', &
code = openFileError, argument = fileName )
END IF
!read number of columns
READ (fileUnit,*,IOSTAT = ios) dummy, layer % jdim
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading columns in file: ', &
code = genericIOError, argument = fileName )
END IF
!read number of rows
READ (fileUnit,*,IOSTAT = ios) dummy, layer % idim
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading rows in file: ', &
code = genericIOError, argument = fileName )
END IF
!read xll corner
READ (fileUnit,*,IOSTAT = ios) dummy, layer % xllcorner
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading xll in file: ', &
code = genericIOError, argument = fileName )
END IF
!read yll corner
READ (fileUnit,*,IOSTAT = ios) dummy, layer % yllcorner
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading yll in file: ', &
code = genericIOError, argument = fileName )
END IF
!read cellsize
READ (fileUnit,*,IOSTAT = ios) dummy, layer % cellsize
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading cellsize in file: ', &
code = genericIOError, argument = fileName )
END IF
!read nodata value
READ (fileUnit,*,IOSTAT = ios) dummy, layer % nodata
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading missing value in file: ', &
code = genericIOError, argument = fileName )
END IF
!allocate grid
ALLOCATE ( layer % mat (layer % idim, layer % jdim), STAT = ios )
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'memory allocation ', &
code = memAllocError,argument = fileName )
ENDIF
!read data
DO i = 1,layer%idim
READ (fileUnit,*) ( layer % mat (i,j) , j=1,layer % jdim )
END DO
CLOSE (fileUnit)
!Set to default other fields
layer % standard_name = ''
layer % long_name = ''
layer % units = ''
layer % varying_mode = 'sequence'
layer % valid_min = layer % nodata
layer % valid_max = layer % nodata
layer % esri_pe_string = ''
layer % reference_time = timeDefault
layer % current_time = timeDefault
layer % next_time = timeDefault
END SUBROUTINE NewGridIntegerFromESRI_ASCII
!==============================================================================
!| Description:
! read a integer grid from a ESRI BINARY file.
SUBROUTINE NewGridIntegerFromESRI_BINARY &
!
(fileName, layer)
USE Utilities, ONLY: &
!Imported routines:
GetUnit
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(in) :: fileName !! file to read
!Arguments with intent(out)
TYPE (grid_integer), INTENT (OUT) :: layer !!returned grid float
!Local variables:
INTEGER (KIND = short) :: fileUnit
INTEGER (KIND = short) :: ios
CHARACTER (LEN = 50) :: dummy
INTEGER (KIND = short) :: recordLength
INTEGER (KIND = short) :: recordNumber
INTEGER (KIND = short) :: i, j
!------------end of declaration------------------------------------------------
!open file
fileUnit = GetUnit ()
OPEN (UNIT = fileUnit, file = TRIM(fileName) // '.hdr', &
STATUS = 'OLD', IOSTAT = ios)
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error opening file: ', &
code = openFileError, argument = TRIM(fileName) // '.hdr' )
END IF
!read number of columns
READ (fileUnit,*,IOSTAT = ios) dummy, layer % jdim
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading columns in file: ', &
code = genericIOError, argument = TRIM(fileName) // '.hdr' )
END IF
!read number of rows
READ (fileUnit,*,IOSTAT = ios) dummy, layer % idim
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading rows in file: ', &
code = genericIOError, argument = TRIM(fileName) // '.hdr' )
END IF
!read xll corner
READ (fileUnit,*,IOSTAT = ios) dummy, layer % xllcorner
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading xll in file: ', &
code = genericIOError, argument = TRIM(fileName) // '.hdr' )
END IF
!read yll corner
READ (fileUnit,*,IOSTAT = ios) dummy, layer % yllcorner
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading yll in file: ', &
code = genericIOError, argument = TRIM(fileName) // '.hdr' )
END IF
!read cellsize
READ (fileUnit,*,IOSTAT = ios) dummy, layer % cellsize
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading cellsize in file: ', &
code = genericIOError, argument = TRIM(fileName) // '.hdr' )
END IF
!read nodata value
READ (fileUnit,*,IOSTAT = ios) dummy, layer % nodata
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error reading missing value in file: ', &
code = genericIOError, argument = TRIM(fileName) // '.hdr' )
END IF
!allocate grid
ALLOCATE ( layer % mat (layer % idim, layer % jdim), STAT = ios )
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'memory allocation ', &
code = memAllocError,argument = fileName )
ENDIF
CLOSE (fileUnit)
fileUnit = GetUnit ()
INQUIRE (IOLENGTH = recordLength) 100_long
OPEN (UNIT = fileUnit, file = TRIM(fileName) // '.flt', &
FORM = 'UNFORMATTED', ACCESS = 'DIRECT', RECL = recordLength, &
STATUS = 'OLD', IOSTAT = ios)
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error opening file: ', &
code = openFileError, argument = TRIM(fileName) // '.flt' )
END IF
!read data
recordNumber = 0
DO i = 1,layer % idim
DO j = 1, layer % jdim
recordNumber = recordNumber + 1
READ (fileUnit,REC = recordNumber) layer % mat (i,j)
END DO
END DO
CLOSE (fileUnit)
!Set other fields to default
layer % standard_name = ''
layer % long_name = ''
layer % units = ''
layer % varying_mode = 'sequence'
layer % valid_min = layer % nodata
layer % valid_max = layer % nodata
layer % esri_pe_string = ''
layer % reference_time = timeDefault
layer % current_time = timeDefault
layer % next_time = timeDefault
END SUBROUTINE NewGridIntegerFromESRI_BINARY
!==============================================================================
!| Description:
! create a new grid_real using an existing grid_real as template
SUBROUTINE NewGridFloatAsGridFloat &
!
(layer, grid, initial)
IMPLICIT NONE
!Arguments with intent(in):
TYPE (grid_real), INTENT(in) :: grid
REAL (KIND = float), OPTIONAL, INTENT(in) :: initial
!Arguments with intent(out):
TYPE (grid_real), INTENT(OUT) :: layer !!grid to be returned
!Local variables:
INTEGER (KIND = short) :: ios
INTEGER (KIND = short) :: i, j
!------------end of declaration------------------------------------------------
layer % jdim = grid % jdim
layer % idim = grid % idim
layer % varying_mode = 'sequence' !default
layer % nodata = MISSING_DEF_REAL
layer % valid_min = layer % nodata
layer % valid_max = layer % nodata
layer % cellsize = grid % cellsize
layer % xllcorner = grid % xllcorner
layer % yllcorner = grid % yllcorner
layer % esri_pe_string = grid % esri_pe_string
layer % grid_mapping = grid % grid_mapping
layer % reference_time = timeDefault
layer % current_time = timeDefault
layer % next_time = timeDefault
ALLOCATE ( layer % mat ( layer % idim, layer % jdim ), STAT = ios )
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'memory allocation ', &
code = memAllocError, argument = 'new grid as' )
ENDIF
DO i = 1, layer % idim
DO j = 1, layer % jdim
IF ( grid % mat (i,j) == grid % nodata ) THEN
layer % mat (i,j) = layer % nodata
ELSE
IF (PRESENT(initial)) THEN
layer % mat (i,j) = initial
ELSE
layer % mat (i,j) = 0.
END IF
END IF
END DO
END DO
END SUBROUTINE NewGridFloatAsGridFloat
!==============================================================================
!| Description:
! create a new grid_real using an existing grid_integer as template
SUBROUTINE NewGridFloatAsGridInteger &
!
(layer, grid, initial)
IMPLICIT NONE
!Arguments with intent(in):
TYPE (grid_integer), INTENT(in) :: grid
REAL (KIND = float), OPTIONAL, INTENT(in) :: initial
!Arguments with intent(out):
TYPE (grid_real), INTENT(OUT) :: layer !!gridreal to return
!Local variables:
INTEGER (KIND = short) :: ios
INTEGER (KIND = short) :: i, j
!------------end of declaration------------------------------------------------
layer % jdim = grid % jdim
layer % idim = grid % idim
layer % varying_mode = 'sequence' !default
layer % nodata = MISSING_DEF_REAL
layer % valid_min = layer % nodata
layer % valid_max = layer % nodata
layer % cellsize = grid % cellsize
layer % xllcorner = grid % xllcorner
layer % yllcorner = grid % yllcorner
layer % esri_pe_string = grid % esri_pe_string
layer % grid_mapping = grid % grid_mapping
layer % reference_time = timeDefault
layer % current_time = timeDefault
layer % next_time = timeDefault
ALLOCATE ( layer % mat ( layer % idim, layer % jdim ), STAT = ios )
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'memory allocation ', &
code = memAllocError, argument = 'new grid as' )
ENDIF
DO i = 1, layer % idim
DO j = 1, layer % jdim
IF ( grid % mat (i,j) == grid % nodata ) THEN
layer % mat (i,j) = layer % nodata
ELSE
IF (PRESENT(initial)) THEN
layer % mat (i,j) = initial
ELSE
layer % mat (i,j) = 0.
END IF
END IF
END DO
END DO
END SUBROUTINE NewGridFloatAsGridInteger
!==============================================================================
!| Description:
! create a new grid_integer using an existing grid_integer as template
SUBROUTINE NewGridIntegerAsGridInteger &
!
(layer, grid, initial)
IMPLICIT NONE
!Arguments with intent(in):
TYPE (grid_integer), INTENT(IN) :: grid
INTEGER, OPTIONAL, INTENT(IN) :: initial
!Arguments with intent(out):
TYPE (grid_integer), INTENT(OUT) :: layer !!grid to be returned
!Local variables:
INTEGER (KIND = short) :: ios
INTEGER (KIND = short) :: i, j
!------------end of declaration------------------------------------------------
layer % jdim = grid % jdim
layer % idim = grid % idim
layer % varying_mode = 'sequence' !default
layer % nodata = MISSING_DEF_INT
layer % valid_min = layer % nodata
layer % valid_max = layer % nodata
layer % cellsize = grid % cellsize
layer % xllcorner = grid % xllcorner
layer % yllcorner = grid % yllcorner
layer % esri_pe_string = grid % esri_pe_string
layer % grid_mapping = grid % grid_mapping
layer % reference_time = timeDefault
layer % current_time = timeDefault
layer % next_time = timeDefault
ALLOCATE ( layer % mat ( layer % idim, layer % jdim ), STAT = ios )
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'memory allocation ', &
code = memAllocError, argument = 'new grid as' )
ENDIF
DO i = 1, layer % idim
DO j = 1, layer % jdim
IF ( grid % mat (i,j) == grid % nodata ) THEN
layer % mat (i,j) = layer % nodata
ELSE
IF (PRESENT(initial)) THEN
layer % mat (i,j) = initial
ELSE
layer % mat (i,j) = 0
END IF
END IF
END DO
END DO
END SUBROUTINE NewGridIntegerAsGridInteger
!==============================================================================
!| Description:
! create a new grid_integer using an existing grid_real as template
SUBROUTINE NewGridIntegerAsGridFloat &
!
(layer, grid, initial)
IMPLICIT NONE
!Arguments with intent(in):
TYPE (grid_real), INTENT(in) :: grid
INTEGER, OPTIONAL, INTENT(in) :: initial
!Arguments with intent (out):
TYPE (grid_integer), INTENT(OUT) :: layer !!grid to be returned
!Local variables:
INTEGER (KIND = short) :: ios
INTEGER (KIND = short) :: i, j
!------------end of declaration------------------------------------------------
layer % jdim = grid % jdim
layer % idim = grid % idim
layer % varying_mode = 'sequence' !default
layer % nodata = MISSING_DEF_INT
layer % valid_min = layer % nodata
layer % valid_max = layer % nodata
layer % cellsize = grid % cellsize
layer % xllcorner = grid % xllcorner
layer % yllcorner = grid % yllcorner
layer % esri_pe_string = grid % esri_pe_string
layer % grid_mapping = grid % grid_mapping
layer % reference_time = timeDefault
layer % current_time = timeDefault
layer % next_time = timeDefault
ALLOCATE ( layer % mat ( layer % idim, layer % jdim ), STAT = ios )
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'memory allocation ', &
code = memAllocError, argument = 'new grid as' )
ENDIF
DO i = 1, layer % idim
DO j = 1, layer % jdim
IF ( grid % mat (i,j) == grid % nodata ) THEN
layer % mat (i,j) = layer % nodata
ELSE
IF (PRESENT(initial)) THEN
layer % mat (i,j) = initial
ELSE
layer % mat (i,j) = 0
END IF
END IF
END DO
END DO
END SUBROUTINE NewGridIntegerAsGridFloat
!==============================================================================
!| Description:
! set the standard name of a float grid
SUBROUTINE SetStandardNameFloat &
!
(name, layer)
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(IN) :: name
!Arguments with intent(out):
TYPE(grid_real), INTENT(INOUT) :: layer
!------------end of declaration------------------------------------------------
layer % standard_name = name
END SUBROUTINE SetStandardNameFloat
!==============================================================================
!| Description:
! set the standard name of a integer grid
SUBROUTINE SetStandardNameInteger &
!
(name, layer)
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(IN) :: name
!Arguments with intent(out):
TYPE(grid_integer), INTENT(INOUT) :: layer
!------------end of declaration------------------------------------------------
layer % standard_name = name
END SUBROUTINE SetStandardNameInteger
!==============================================================================
!| Description:
! set the long name of a float grid
SUBROUTINE SetLongNameFloat &
!
(name, layer)
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(IN) :: name
!Arguments with intent(out):
TYPE(grid_real), INTENT(INOUT) :: layer
!------------end of declaration------------------------------------------------
layer % long_name = name
END SUBROUTINE SetLongNameFloat
!==============================================================================
!| Description:
! set the long name of a integer grid
SUBROUTINE SetLongNameInteger &
!
(name, layer)
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(IN) :: name
!Arguments with intent(out):
TYPE(grid_integer), INTENT(INOUT) :: layer
!------------end of declaration------------------------------------------------
layer % long_name = name
END SUBROUTINE SetLongNameInteger
!==============================================================================
!| Description:
! set the units of a float grid
SUBROUTINE SetUnitsFloat &
!
(units, layer)
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(IN) :: units
!Arguments with intent(out):
TYPE(grid_real), INTENT(INOUT) :: layer
!------------end of declaration------------------------------------------------
layer % units = units
END SUBROUTINE SetUnitsFloat
!==============================================================================
!| Description:
! set the units of a integer grid
SUBROUTINE SetUnitsInteger &
!
(units, layer)
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(IN) :: units
!Arguments with intent(out):
TYPE(grid_integer), INTENT(INOUT) :: layer
!------------end of declaration------------------------------------------------
layer % units = units
END SUBROUTINE SetUnitsInteger
!==============================================================================
!| Description:
! set the varying mode of a float grid
! Possible values:
!
! * linear: linear variation between two dates
! * sequence: new grid substitute the previous as frames in a movie
!
SUBROUTINE SetVaryingModeFloat &
!
(varMod, layer)
USE StringManipulation, ONLY: &
!Imported routines:
StringToLower, StringCompact
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(IN) :: varMod
!Arguments with intent(out):
TYPE(grid_real), INTENT(INOUT) :: layer
!Local variables
CHARACTER (LEN = 20) :: string
!------------end of declaration------------------------------------------------
string = StringCompact (StringToLower (varMod) )
IF ( String == 'sequence' .OR. String == 'linear' ) THEN
layer % varying_mode = string
ELSE
CALL Catch ('error', 'GridLib', 'unsupported varying mode: ', &
code = unknownOption, argument = string )
END IF
END SUBROUTINE SetVaryingModeFloat
!==============================================================================
!| Description:
! set the varying mode of a integer grid
SUBROUTINE SetVaryingModeInteger &
!
(varMod, layer)
USE StringManipulation, ONLY: &
!Imported routines:
StringToLower, StringCompact
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(IN) :: varMod
!Arguments with intent(out):
TYPE(grid_integer), INTENT(INOUT) :: layer
!Local variables
CHARACTER (LEN = 20) :: string
!------------end of declaration------------------------------------------------
string = StringCompact (StringToLower (varMod) )
IF ( String == 'sequence' .OR. String == 'linear' ) THEN
layer % varying_mode = string
ELSE
CALL Catch ('error', 'GridLib', 'unsupported varying mode: ', &
code = unknownOption, argument = string )
END IF
END SUBROUTINE SetVaryingModeInteger
!==============================================================================
!| Description:
! set the reference time of a float grid
SUBROUTINE SetReferenceTimeFloat &
!
(time, layer)
IMPLICIT NONE
!Arguments with intent(in):
TYPE (DateTime), INTENT(IN) :: time
!Arguments with intent(out):
TYPE(grid_real), INTENT(INOUT) :: layer
!------------end of declaration------------------------------------------------
layer % reference_time = time
END SUBROUTINE SetReferenceTimeFloat
!==============================================================================
!| Description:
! set the reference time of a integer grid
SUBROUTINE SetReferenceTimeInteger &
!
(time, layer)
IMPLICIT NONE
!Arguments with intent(in):
TYPE (DateTime), INTENT(IN) :: time
!Arguments with intent(out):
TYPE(grid_integer), INTENT(INOUT) :: layer
!------------end of declaration------------------------------------------------
layer % reference_time = time
END SUBROUTINE SetReferenceTimeInteger
!==============================================================================
!| Description:
! set the current time of a float grid
SUBROUTINE SetCurrentTimeFloat &
!
(time, layer)
IMPLICIT NONE
!Arguments with intent(in):
TYPE (DateTime), INTENT(IN) :: time
!Arguments with intent(out):
TYPE(grid_real), INTENT(INOUT) :: layer
!------------end of declaration------------------------------------------------
layer % current_time = time
END SUBROUTINE SetCurrentTimeFloat
!==============================================================================
!| Description:
! set the current time of a integer grid
SUBROUTINE SetCurrentTimeInteger &
!
(time, layer)
IMPLICIT NONE
!Arguments with intent(in):
TYPE (DateTime), INTENT(IN) :: time
!Arguments with intent(out):
TYPE(grid_integer), INTENT(INOUT) :: layer
!------------end of declaration------------------------------------------------
layer % current_time = time
END SUBROUTINE SetCurrentTimeInteger
!==============================================================================
!| Description:
! set the esri pe string of a float grid
SUBROUTINE SetEsriPeStringFloat &
!
(string, layer)
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(IN) :: string
!Arguments with intent(out):
TYPE(grid_real), INTENT(INOUT) :: layer
!------------end of declaration------------------------------------------------
layer % esri_pe_string = string
END SUBROUTINE SetEsriPeStringFloat
!==============================================================================
!| Description:
! set the esri pe string of a integer grid
SUBROUTINE SetEsriPeStringInteger &
!
(string, layer)
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(IN) :: string
!Arguments with intent(out):
TYPE(grid_integer), INTENT(INOUT) :: layer
!------------end of declaration------------------------------------------------
layer % esri_pe_string = string
END SUBROUTINE SetEsriPeStringInteger
!==============================================================================
!| Description:
! export grid into netcdf file. Two actions are possible:
!
! * add: add a non-record variable to a netCDF dataset. If file does not exist
! it is created new. The added variable
! must have the same dimensions of already present variables.
! If you try to add a variable already present in netCDF dataset
! an error is returned.
! * append: append more data to record variables along the unlimited dimension.
! If netCDF dataset is created new, dimensions and attributes
! are set, otherwise only new data are written. If current time
! is already present in netcdf dataset an error is returned
!
SUBROUTINE ExportGridFloatToNetCDF &
!
(grid, file, name, action)
USE StringManipulation, ONLY: &
! imported routines:
StringToUpper, ToString
IMPLICIT NONE
! Arguments with intent(in):
TYPE (grid_real), INTENT(IN) :: grid !! grid to be exported
CHARACTER (LEN = *), INTENT(IN) :: file !!netcdf file to export to
CHARACTER (LEN = *), INTENT(IN) :: name !!name of variable in netcdf
CHARACTER (LEN = *), INTENT(IN) :: action !!add or append
! Local variables:
INTEGER (KIND = short) :: ncStatus !!error code return by NetCDF routines
INTEGER (KIND = short) :: ncId !!NetCdf Id for the file
INTEGER (KIND = short) :: dimXid !!id of x dimension
INTEGER (KIND = short) :: dimYid !!id of y dimension
INTEGER (KIND = short) :: dimTid !!id of time dimension
INTEGER (KIND = short) :: varXid !!id of x variable
INTEGER (KIND = short) :: varYid !!id of y variable
INTEGER (KIND = short) :: varTid !!id of time variable
INTEGER (KIND = short) :: mapId !!id of grid mapping variable
INTEGER (KIND = short), ALLOCATABLE :: varDim (:) !!define dimension of variable
INTEGER (KIND = short) :: varId !!id of grid variable
CHARACTER (LEN = 25) :: string
REAL (KIND = float), ALLOCATABLE :: lats(:), lons(:) !!array containing coordinate
INTEGER (KIND = short) :: i !!loop index
LOGICAL :: fileExist !!true if netcdf exists
LOGICAL :: varExist !!true if variable exists in dataset
TYPE (dateTime) :: ref_time !!reference time to calculate time span
CHARACTER (LEN = 7) :: dummyString
REAL (KIND = float) :: timeSpan !!time between current time and reference time
INTEGER (KIND = short) :: records !!number of records stored in netcdf dataset
INTEGER (KIND = short) :: slice (3) !!used to write new record in right position
INTEGER (KIND = short) :: timeRecord (1) !!to write new time record
REAL (KIND = float), ALLOCATABLE :: times (:) !!values stored in time variable
LOGICAL :: timeExists
REAL (KIND = float), ALLOCATABLE :: tempGrid (:,:)
!------------end of declaration------------------------------------------------
!verify if file exists
ncStatus = nf90_open (file, NF90_WRITE, ncId)
IF (ncStatus == nf90_noerr) THEN
fileExist = .TRUE.
ELSE
fileExist = .FALSE.
END IF
!if file does not exist create the file
IF (.NOT. fileExist) THEN
ncStatus = nf90_create ( file, NF90_CLOBBER, ncId )
CALL ncErrorHandler (ncStatus)
!define dimensions
ncStatus = nf90_def_dim ( ncId, 'x', grid % jdim, dimXid )
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_def_dim ( ncId, 'y', grid % idim, dimYid )
CALL ncErrorHandler (ncStatus)
!if record (unlimited) variable define time dimension
IF ( StringToUpper(action) == 'APPEND' ) THEN
ncStatus = nf90_def_dim ( ncId, 'time', NF90_UNLIMITED, dimTid )
CALL ncErrorHandler (ncStatus)
END IF
!define coordinate variables
ncStatus = nf90_def_var (ncId, 'x', NF90_FLOAT, dimXid, varXid)
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_def_var (ncId, 'y', NF90_FLOAT, dimYid, varYid)
CALL ncErrorHandler (ncStatus)
!if record (unlimited) variable define time variable
IF ( StringToUpper(action) == 'APPEND' ) THEN
ncStatus = nf90_def_var ( ncId, 'time', NF90_INT, dimTid, varTid )
CALL ncErrorHandler (ncStatus)
END IF
!assign attributes to coordinate variables
IF (grid % grid_mapping % system == GEODETIC) THEN
ncStatus = nf90_put_att (ncId, varXid, 'long_name', 'longitude (centre of grid cell)')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varXid, 'standard_name', 'longitude')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varXid, 'units', 'deg')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varXid, 'axis', 'X')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varYid, 'long_name', 'latitude (centre of grid cell)')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varYid, 'standard_name', 'latitude')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varYid, 'units', 'deg')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varYid, 'axis', 'Y')
CALL ncErrorHandler (ncStatus)
ELSE
ncStatus = nf90_put_att (ncId, varXid, 'long_name', 'x coordinate of projection')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varXid, 'standard_name', 'projection_x_coordinate')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varXid, 'units', 'm')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varXid, 'axis', 'X')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varYid, 'long_name', 'y coordinate of projection')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varYid, 'standard_name', 'projection_y_coordinate')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varYid, 'units', 'm')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varYid, 'axis', 'Y')
CALL ncErrorHandler (ncStatus)
END IF
!if record (unlimited) variable define attributes of time variable
IF ( StringToUpper(action) == 'APPEND' ) THEN
ncStatus = nf90_put_att ( ncId, varTid, 'long_name', 'time' )
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att ( ncId, varTid, 'standard_name', 'time' )
CALL ncErrorHandler (ncStatus)
string = grid % current_time
ncStatus = nf90_put_att ( ncId, varTid, 'units', 'seconds since ' // string )
CALL ncErrorHandler (ncStatus)
END IF
!define dummy grid mapping variable
ncStatus = nf90_def_var (ncId, 'crs', NF90_INT, mapId)
CALL ncErrorHandler (ncStatus)
!assign attributes to grid mapping variable
ncStatus = nf90_put_att (ncId, mapId, 'grid_mapping_name', &
grid % grid_mapping % name)
CALL ncErrorHandler (ncStatus)
DO i = 1, grid % grid_mapping % basic
ncStatus = nf90_put_att (ncId, mapId, grid % grid_mapping % &
description (i), grid % grid_mapping % param (i))
CALL ncErrorHandler (ncStatus)
END DO
!datum
ncStatus = nf90_put_att (ncId, mapId, 'datum_code', &
grid % grid_mapping % datum % code)
CALL ncErrorHandler (ncStatus)
!assign global attributes
ncStatus = nf90_put_att (ncId, NF90_GLOBAL, 'Conventions', 'CF-1.0')
CALL ncErrorHandler (ncStatus)
string = UtcNow ()
ncStatus = nf90_put_att (ncId, NF90_GLOBAL, 'history', 'creation date: ' // string )
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, NF90_GLOBAL, 'institution', 'Politecnico di Milano' )
CALL ncErrorHandler (ncStatus)
!end define mode
ncStatus = nf90_enddef (ncId)
CALL ncErrorHandler (ncStatus)
END IF
!enter define mode
ncStatus = nf90_redef (ncId)
CALL ncErrorHandler (ncStatus)
!define variable for the grid
!If file already exists, have to read dimXd and dimYd
!and dimTid if necessary
IF ( fileExist) THEN
ncStatus = nf90_inq_dimid (ncId, "x", dimXid)
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_inq_dimid (ncId, "y", dimYid)
CALL ncErrorHandler (ncStatus)
IF (StringToUpper (action) == 'APPEND') THEN
ncStatus = nf90_inq_dimid (ncId, "time", dimTid)
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_inq_varid (ncId, 'time', varTid)
CALL ncErrorHandler (ncStatus)
END IF
END IF
!verify if variable already exists
varId = 0
ncStatus = nf90_inq_varid (ncId, name, varId)
IF (ncStatus == nf90_noerr) THEN
varExist = .TRUE.
!if variable already exists in non record dataset: error
IF (StringToUpper (action) == 'ADD') THEN
CALL Catch ('error', 'GridLib', 'variable ' // TRIM(name) // &
' already exists in netCDf dataset: ', &
code = ncIOError, argument = file )
END IF
ELSE
varExist = .FALSE.
!define new variable
IF (StringToUpper (action) == 'ADD' ) THEN
ALLOCATE ( varDim (2) )
ELSE IF (StringToUpper (action) == 'APPEND' ) THEN
ALLOCATE ( varDim (3) )
varDim(3) = dimTid
END IF
varDim(1) = dimXid
varDim(2) = dimYid
ncStatus = nf90_def_var (ncId, name, NF90_FLOAT, varDim, varId)
CALL ncErrorHandler (ncStatus)
DEALLOCATE (varDim)
!assign attributes to variable
ncStatus = nf90_put_att (ncId, varId, 'long_name', grid % long_name)
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varId, 'standard_name', grid % standard_name)
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varId, 'units', grid % units)
CALL ncErrorHandler (ncStatus)
IF (grid % valid_min /= grid % nodata) THEN
ncStatus = nf90_put_att (ncId, varId, 'valid_min', grid % valid_min)
CALL ncErrorHandler (ncStatus)
END IF
IF (grid % valid_max /= grid % nodata) THEN
ncStatus = nf90_put_att (ncId, varId, 'valid_max', grid % valid_max)
CALL ncErrorHandler (ncStatus)
END IF
ncStatus = nf90_put_att (ncId, varId, '_FillValue', grid % nodata)
CALL ncErrorHandler (ncStatus)
IF (grid % esri_pe_string /= '') THEN
ncStatus = nf90_put_att (ncId, varId, 'esri_pe_string', grid % esri_pe_string)
CALL ncErrorHandler (ncStatus)
END IF
ncStatus = nf90_put_att (ncId, varId, 'varying_mode', grid % varying_mode)
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varId, 'grid_mapping', 'crs')
CALL ncErrorHandler (ncStatus)
END IF
!end define mode
ncStatus = nf90_enddef (ncId)
CALL ncErrorHandler (ncStatus)
IF ( .NOT. fileExist) THEN !if file was created new, add coordinate variables
!put coordinate
ALLOCATE ( lons ( grid % jdim) )
DO i = 1, grid % jdim
lons (i) = grid % xllcorner + i * grid % cellsize - grid % cellsize / 2.
!lons (i) = int(10000*lons (i) + .5) / 10000.0
END DO
ncStatus = nf90_put_var (ncId, varXid, lons )
CALL ncErrorHandler (ncStatus)
DEALLOCATE ( lons )
ALLOCATE ( lats ( grid % idim) )
DO i = 1, grid % idim
lats (i) = grid % yllcorner + i * grid % cellsize - grid % cellsize / 2.
!lats (i) = int(10000*lats (i) + .5) / 10000.0
END DO
ncStatus = nf90_put_var (ncId, varYid, lats )
CALL ncErrorHandler (ncStatus)
DEALLOCATE ( lats )
END IF
!if unlimited variable put time and grid
IF ( StringToUpper (action) == 'APPEND' ) THEN
!time
CALL ParseTime (ncId, ref_time, dummyString)
timeSpan = grid % current_time - ref_time
!retrieve number of records already stored in dataset
ncStatus = nf90_inquire_dimension (ncId, dimTid, len = records)
CALL ncErrorHandler (ncStatus)
!check if time span already exists
ALLOCATE ( times (records) )
ncStatus = nf90_get_var (ncId, varTid, times)
timeExists = .FALSE.
DO i = 1, records
IF ( timeSpan == times (i) ) THEN
timeExists = .TRUE.
timeRecord = i
!(ADD SUPPORT FOR MULTIPLE TIME AXIS
!CALL Catch ('error', 'GridLib', &
! 'time is already present in netCDF dataset: ', &
! code = ncIOError, argument = ToString(timeSpan) )
END IF
END DO
DEALLOCATE (times)
!put time span
IF ( .NOT. timeExists ) THEN
timeRecord = records + 1
ncStatus = nf90_put_var (ncId, varTid, timeSpan, start = timeRecord )
END IF
!put grid variable
slice = (/ 1, 1, timeRecord /)
!swap grid before write to netcdf
ALLOCATE (tempGrid (grid % jdim, grid % idim) )
CALL SwapGridRealBack (grid % mat, tempGrid)
!ncStatus = nf90_put_var (ncId, varId, tempGrid, start = slice, count = (/1,1,1/) )
ncStatus = nf90_put_var (ncId, varId, tempGrid, start = slice)
CALL ncErrorHandler (ncStatus)
DEALLOCATE (tempGrid)
ELSE IF (StringToUpper (action) == 'ADD' ) THEN
!swap grid before write to netcdf
ALLOCATE (tempGrid (grid % jdim, grid % idim) )
CALL SwapGridRealBack (grid % mat, tempGrid)
!put variable
ncStatus = nf90_put_var (ncId, varId, tempGrid )
CALL ncErrorHandler (ncStatus)
DEALLOCATE (tempGrid)
END IF
! close file
ncStatus = nf90_close (ncid)
CALL ncErrorHandler (ncStatus)
END SUBROUTINE ExportGridFloatToNetCDF
!==============================================================================
!| Description:
! export grid into netcdf file. Two actions are possible:
!
! * add: add a non-record variable to a netCDF dataset. If file does not exist
! it is created new. The added variable
! must have the same dimensions of already present variables.
! If you try to add a variable already present in netCDF dataset
! an error is returned.
! * append: append more data to record variables along the unlimited dimension.
! If netCDF dataset is created new, dimensions and attributes
! are set, otherwise only new data are written. If current time
! is already present in netcdf dataset an error is returned
!
SUBROUTINE ExportGridIntegerToNetCDF &
!
(grid, file, name, action)
USE StringManipulation, ONLY: &
! imported routines:
StringToUpper, ToString
IMPLICIT NONE
! Arguments with intent(in):
TYPE (grid_integer), INTENT(IN) :: grid !! grid to be exported
CHARACTER (LEN = *), INTENT(IN) :: file !!netcdf file to export to
CHARACTER (LEN = *), INTENT(IN) :: name !!name of variable in netcdf
CHARACTER (LEN = *), INTENT(IN) :: action !!add or append
! Local variables:
INTEGER (KIND = short) :: ncStatus !!error code return by NetCDF routines
INTEGER (KIND = short) :: ncId !!NetCdf Id for the file
INTEGER (KIND = short) :: dimXid !!id of x dimension
INTEGER (KIND = short) :: dimYid !!id of y dimension
INTEGER (KIND = short) :: dimTid !!id of time dimension
INTEGER (KIND = short) :: varXid !!id of x variable
INTEGER (KIND = short) :: varYid !!id of y variable
INTEGER (KIND = short) :: varTid !!id of time variable
INTEGER (KIND = short) :: mapId !!id of grid mapping variable
INTEGER (KIND = short), ALLOCATABLE :: varDim (:) !!define dimension of variable
INTEGER (KIND = short) :: varId !!id of grid variable
CHARACTER (LEN = 25) :: string
REAL (KIND = float), ALLOCATABLE :: lats(:), lons(:) !!array containing coordinate
INTEGER (KIND = short) :: i !!loop index
LOGICAL :: fileExist !!true if netcdf exists
LOGICAL :: varExist !!true if variable exists in dataset
TYPE (dateTime) :: ref_time !!reference time to calculate time span
CHARACTER (LEN = 7) :: dummyString
REAL (KIND = float) :: timeSpan !!time between current time and reference time
INTEGER (KIND = short) :: records !!number of records stored in netcdf dataset
INTEGER (KIND = short) :: slice (3) !!used to write new record in right position
INTEGER (KIND = short) :: timeRecord (1) !!to write new time record
REAL (KIND = float), ALLOCATABLE :: times (:) !!values stored in time variable
LOGICAL :: timeExists
INTEGER (KIND = long), ALLOCATABLE :: tempGrid (:,:)
!------------end of declaration------------------------------------------------
!verify if file exists
ncStatus = nf90_open (file, NF90_WRITE, ncId)
IF (ncStatus == nf90_noerr) THEN
fileExist = .TRUE.
ELSE
fileExist = .FALSE.
END IF
!if file does not exist create the file
IF (.NOT. fileExist) THEN
ncStatus = nf90_create ( file, NF90_CLOBBER, ncId )
CALL ncErrorHandler (ncStatus)
!define dimensions
ncStatus = nf90_def_dim ( ncId, 'x', grid % jdim, dimXid )
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_def_dim ( ncId, 'y', grid % idim, dimYid )
CALL ncErrorHandler (ncStatus)
!if record (unlimited) variable define time dimension
IF ( StringToUpper(action) == 'APPEND' ) THEN
ncStatus = nf90_def_dim ( ncId, 'time', NF90_UNLIMITED, dimTid )
CALL ncErrorHandler (ncStatus)
END IF
!define coordinate variables
ncStatus = nf90_def_var (ncId, 'x', NF90_FLOAT, dimXid, varXid)
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_def_var (ncId, 'y', NF90_FLOAT, dimYid, varYid)
CALL ncErrorHandler (ncStatus)
!if record (unlimited) variable define time variable
IF ( StringToUpper(action) == 'APPEND' ) THEN
ncStatus = nf90_def_var ( ncId, 'time', NF90_FLOAT, dimTid, varTid )
CALL ncErrorHandler (ncStatus)
END IF
!assign attributes to coordinate variables
IF (grid % grid_mapping % system == GEODETIC) THEN
ncStatus = nf90_put_att (ncId, varXid, 'long_name', 'longitude (centre of grid cell)')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varXid, 'standard_name', 'longitude')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varXid, 'units', 'deg')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varXid, 'axis', 'X')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varYid, 'long_name', 'latitude (centre of grid cell)')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varYid, 'standard_name', 'latitude')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varYid, 'units', 'deg')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varYid, 'axis', 'Y')
CALL ncErrorHandler (ncStatus)
ELSE
ncStatus = nf90_put_att (ncId, varXid, 'long_name', 'x coordinate of projection')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varXid, 'standard_name', 'projection_x_coordinate')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varXid, 'units', 'm')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varXid, 'axis', 'X')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varYid, 'long_name', 'y coordinate of projection')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varYid, 'standard_name', 'projection_y_coordinate')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varYid, 'units', 'm')
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varYid, 'axis', 'Y')
CALL ncErrorHandler (ncStatus)
END IF
!if record (unlimited) variable define attributes of time variable
IF ( StringToUpper(action) == 'APPEND' ) THEN
ncStatus = nf90_put_att ( ncId, varTid, 'long_name', 'time' )
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att ( ncId, varTid, 'standard_name', 'time' )
CALL ncErrorHandler (ncStatus)
string = grid % current_time
ncStatus = nf90_put_att ( ncId, varTid, 'units', 'seconds since ' // string )
CALL ncErrorHandler (ncStatus)
END IF
!define dummy grid mapping variable
ncStatus = nf90_def_var (ncId, 'crs', NF90_INT, mapId)
CALL ncErrorHandler (ncStatus)
!assign attributes to grid mapping variable
ncStatus = nf90_put_att (ncId, mapId, 'grid_mapping_name', &
grid % grid_mapping % name)
CALL ncErrorHandler (ncStatus)
DO i = 1, grid % grid_mapping % basic
ncStatus = nf90_put_att (ncId, mapId, grid % grid_mapping % description (i),&
grid % grid_mapping % param (i))
CALL ncErrorHandler (ncStatus)
END DO
!datum
ncStatus = nf90_put_att (ncId, mapId, 'datum_code', &
grid % grid_mapping % datum % code)
CALL ncErrorHandler (ncStatus)
!assign global attributes
ncStatus = nf90_put_att (ncId, NF90_GLOBAL, 'Conventions', 'CF-1.0')
CALL ncErrorHandler (ncStatus)
string = UtcNow ()
ncStatus = nf90_put_att (ncId, NF90_GLOBAL, 'history', 'creation date: ' // string )
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, NF90_GLOBAL, 'institution', 'Politecnico di Milano' )
CALL ncErrorHandler (ncStatus)
!end define mode
ncStatus = nf90_enddef (ncId)
CALL ncErrorHandler (ncStatus)
END IF
!enter define mode
ncStatus = nf90_redef (ncId)
CALL ncErrorHandler (ncStatus)
!define variable for the grid
!If file already exists, have to read dimXd and dimYd
!and dimTid if necessary
IF ( fileExist) THEN
ncStatus = nf90_inq_dimid (ncId, "x", dimXid)
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_inq_dimid (ncId, "y", dimYid)
CALL ncErrorHandler (ncStatus)
IF (StringToUpper (action) == 'APPEND') THEN
ncStatus = nf90_inq_dimid (ncId, "time", dimTid)
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_inq_varid (ncId, 'time', varTid)
CALL ncErrorHandler (ncStatus)
END IF
END IF
!verify if variable already exists
varId = 0
ncStatus = nf90_inq_varid (ncId, name, varId)
IF (ncStatus == nf90_noerr) THEN
varExist = .TRUE.
!if variable already exists in non record dataset: error
IF (StringToUpper (action) == 'ADD') THEN
CALL Catch ('error', 'GridLib', 'variable ' // TRIM(name) // &
' already exists in netCDf dataset: ', &
code = ncIOError, argument = file )
END IF
ELSE
varExist = .FALSE.
!define new variable
IF (StringToUpper (action) == 'ADD' ) THEN
ALLOCATE ( varDim (2) )
ELSE IF (StringToUpper (action) == 'APPEND' ) THEN
ALLOCATE ( varDim (3) )
varDim(3) = dimTid
END IF
varDim(1) = dimXid
varDim(2) = dimYid
ncStatus = nf90_def_var (ncId, name, NF90_FLOAT, varDim, varId)
CALL ncErrorHandler (ncStatus)
DEALLOCATE (varDim)
!assign attributes to variable
ncStatus = nf90_put_att (ncId, varId, 'long_name', grid % long_name)
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varId, 'standard_name', grid % standard_name)
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varId, 'units', grid % units)
CALL ncErrorHandler (ncStatus)
IF (grid % valid_min /= grid % nodata) THEN
ncStatus = nf90_put_att (ncId, varId, 'valid_min', grid % valid_min)
CALL ncErrorHandler (ncStatus)
END IF
IF (grid % valid_max /= grid % nodata) THEN
ncStatus = nf90_put_att (ncId, varId, 'valid_max', grid % valid_max)
CALL ncErrorHandler (ncStatus)
END IF
ncStatus = nf90_put_att (ncId, varId, '_FillValue', grid % nodata)
CALL ncErrorHandler (ncStatus)
IF (grid % esri_pe_string /= '') THEN
ncStatus = nf90_put_att (ncId, varId, 'esri_pe_string', grid % esri_pe_string)
CALL ncErrorHandler (ncStatus)
END IF
ncStatus = nf90_put_att (ncId, varId, 'varying_mode', grid % varying_mode)
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_put_att (ncId, varId, 'grid_mapping', 'crs')
CALL ncErrorHandler (ncStatus)
END IF
!end define mode
ncStatus = nf90_enddef (ncId)
CALL ncErrorHandler (ncStatus)
IF ( .NOT. fileExist) THEN !if file was created new, add coordinate variables
!put coordinate
ALLOCATE ( lons ( grid % jdim) )
DO i = 1, grid % jdim
lons (i) = grid % xllcorner + i * grid % cellsize - grid % cellsize / 2.
END DO
ncStatus = nf90_put_var (ncId, varXid, lons )
CALL ncErrorHandler (ncStatus)
DEALLOCATE ( lons )
ALLOCATE ( lats ( grid % idim) )
DO i = 1, grid % idim
lats (i) = grid % yllcorner + i * grid % cellsize - grid % cellsize / 2.
END DO
ncStatus = nf90_put_var (ncId, varYid, lats )
CALL ncErrorHandler (ncStatus)
DEALLOCATE ( lats )
END IF
!if unlimited variable put time and grid
IF ( StringToUpper (action) == 'APPEND' ) THEN
!time
CALL ParseTime (ncId, ref_time, dummyString)
timeSpan = grid % current_time - ref_time
!retrieve number of records already stored in dataset
ncStatus = nf90_inquire_dimension (ncId, dimTid, len = records)
CALL ncErrorHandler (ncStatus)
!check if time span already exists
ALLOCATE ( times (records) )
ncStatus = nf90_get_var (ncId, varTid, times)
timeExists = .FALSE.
DO i = 1, records
IF ( timeSpan == times (i) ) THEN
timeExists = .TRUE.
timeRecord = i
!(ADD SUPPORT FOR MULTIPLE TIME AXIS
!CALL Catch ('error', 'GridLib', &
! 'time is already present in netCDF dataset: ', &
! code = ncIOError, argument = ToString(timeSpan) )
END IF
END DO
DEALLOCATE (times)
!put time span
IF ( .NOT. timeExists ) THEN
timeRecord = records + 1
ncStatus = nf90_put_var (ncId, varTid, timeSpan, start = timeRecord )
END IF
!put grid variable
slice = (/ 1, 1, timeRecord /)
!swap grid before write to netcdf
ALLOCATE (tempGrid (grid % jdim, grid % idim) )
CALL SwapGridIntegerBack (grid % mat, tempGrid)
ncStatus = nf90_put_var (ncId, varId, tempGrid, start = slice )
CALL ncErrorHandler (ncStatus)
DEALLOCATE (tempGrid)
ELSE IF (StringToUpper (action) == 'ADD' ) THEN
!swap grid before write to netcdf
ALLOCATE (tempGrid (grid % jdim, grid % idim) )
CALL SwapGridIntegerBack (grid % mat, tempGrid)
!put variable
ncStatus = nf90_put_var (ncId, varId, tempGrid )
CALL ncErrorHandler (ncStatus)
DEALLOCATE (tempGrid)
END IF
! close file
ncStatus = nf90_close (ncid)
CALL ncErrorHandler (ncStatus)
END SUBROUTINE ExportGridIntegerToNetCDF
!==============================================================================
!| Description:
! export grid_real to file.
! List of supported format:
!
! * `ESRI_ASCII`: ESRI ASCII GRID
! * `ESRI_BINARY`: ESRI BINARY GRID
!
SUBROUTINE ExportGridFloatToFile &
!
(layer, fileName, fileFormat)
IMPLICIT NONE
!Arguments with intent(in):
TYPE (grid_real), INTENT (IN) :: layer
CHARACTER (LEN = *), INTENT (IN) :: fileName
INTEGER (KIND = short), INTENT(IN) :: fileFormat
!------------end of declaration------------------------------------------------
IF ( fileformat == ESRI_ASCII ) THEN
CALL ExportGridFloatToESRI_ASCII (layer, fileName)
ELSE IF ( fileformat == ESRI_BINARY ) THEN
CALL ExportGridFloatToESRI_BINARY (layer, fileName)
ELSE
CALL Catch ('error', 'GridLib', &
'unknown option on exporting file: ', &
code = unknownOption, argument = fileName )
END IF
END SUBROUTINE ExportGridFloatToFile
!==============================================================================
!| Description:
! export grid_real to a ESRI ASCII file.
SUBROUTINE ExportGridFloatToESRI_ASCII &
!
(layer, fileName)
USE Utilities, ONLY: &
!Imported routines:
GetUnit
IMPLICIT NONE
!Arguments with intent(in):
TYPE (grid_real), INTENT (IN) :: layer
CHARACTER (LEN = *), INTENT (IN) :: fileName
!Local variables:
INTEGER (KIND = short) :: fileUnit
INTEGER (KIND = short) :: ios
INTEGER (KIND = short) :: i,j
!------------end of declaration------------------------------------------------
!open file
fileUnit = GetUnit ()
OPEN (UNIT = fileUnit, file = fileName, IOSTAT = ios)
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error opening file: ', &
code = openFileError, argument = fileName )
END IF
!write header
WRITE(fileUnit,'(a14,i10)') "ncols ", layer % jdim
WRITE(fileUnit,'(a14,i10)') "nrows ", layer % idim
WRITE(fileUnit,'(a14,f15.5)') "xllcorner ", layer % xllcorner
WRITE(fileUnit,'(a14,f15.5)') "yllcorner ", layer % yllcorner
WRITE(fileUnit,'(a14,f15.5)') "cellsize ", layer % cellsize
WRITE(fileUnit,'(a14,E14.7)') "NODATA_value ", layer % nodata
!write data
DO i = 1, layer % idim
DO j = 1, layer % jdim - 1
WRITE(fileUnit,'(E14.7," ")', ADVANCE = 'no') layer % mat(i,j)
END DO
WRITE(fileUnit,'(E14.7," ")') layer % mat(i,layer % jdim)
END DO
CLOSE (fileUnit)
END SUBROUTINE ExportGridFloatToESRI_ASCII
!==============================================================================
!| Description:
! export grid_real to a ESRI BINARY file.
SUBROUTINE ExportGridFloatToESRI_BINARY &
!
(layer, fileName)
USE Utilities, ONLY: &
!Imported routines:
GetUnit
IMPLICIT NONE
!Arguments with intent(in):
TYPE (grid_real), INTENT (IN) :: layer
CHARACTER (LEN = *), INTENT (IN) :: fileName
!Local variables:
INTEGER (KIND = short) :: fileUnit
INTEGER (KIND = short) :: ios
INTEGER (KIND = short) :: recordLength
INTEGER (KIND = short) :: recordNumber
INTEGER (KIND = short) :: i,j
!------------end of declaration------------------------------------------------
!open file
fileUnit = GetUnit ()
OPEN (UNIT = fileUnit, file = TRIM(fileName) // '.hdr', IOSTAT = ios)
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error opening file: ', &
code = openFileError, argument = TRIM(fileName) // '.hdr' )
END IF
!write header
WRITE(fileUnit,'(a14,i10)') "ncols ", layer % jdim
WRITE(fileUnit,'(a14,i10)') "nrows ", layer % idim
WRITE(fileUnit,'(a14,f15.5)') "xllcorner ", layer % xllcorner
WRITE(fileUnit,'(a14,f15.5)') "yllcorner ", layer % yllcorner
WRITE(fileUnit,'(a14,f15.5)') "cellsize ", layer % cellsize
WRITE(fileUnit,'(a14,E14.7)') "NODATA_value ", layer % nodata
WRITE(fileUnit,'(a18)') "BYTEORDER LSBFIRST"
CLOSE (fileUnit)
!write data
fileUnit = GetUnit ()
INQUIRE (IOLENGTH = recordLength) 100_float
OPEN (UNIT = fileUnit, file = TRIM(fileName) // '.flt', &
FORM = 'UNFORMATTED', ACCESS = 'DIRECT', RECL = recordLength, &
IOSTAT = ios)
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error opening file: ', &
code = openFileError, argument = TRIM(fileName) // '.flt' )
END IF
recordNumber = 0
DO i = 1,layer % idim
DO j = 1, layer % jdim
recordNumber = recordNumber + 1
WRITE (fileUnit,REC = recordNumber) layer % mat (i,j)
END DO
END DO
CLOSE (fileUnit)
END SUBROUTINE ExportGridFloatToESRI_BINARY
!==============================================================================
!| Description:
! export grid_integer to file.
! List of supported format:
!
! * `ESRI_ASCII`: ESRI ASCII GRID
! * `ESRI_BINARY`: ESRI BINARY GRID
!
SUBROUTINE ExportGridIntegerToFile &
!
(layer, fileName, fileFormat)
IMPLICIT NONE
!Arguments with intent(in):
TYPE (grid_integer), INTENT (IN) :: layer
CHARACTER (LEN = *), INTENT (IN) :: fileName
INTEGER (KIND = short), INTENT(IN) :: fileFormat
!------------end of declaration------------------------------------------------
IF ( fileformat == ESRI_ASCII ) THEN
CALL ExportGridIntegerToESRI_ASCII (layer, fileName)
ELSE IF ( fileformat == ESRI_BINARY ) THEN
CALL ExportGridIntegerToESRI_BINARY (layer, fileName)
ELSE
CALL Catch ('error', 'GridLib', &
'unknown option on exporting file: ', &
code = unknownOption, argument = fileName )
END IF
END SUBROUTINE ExportGridIntegerToFile
!==============================================================================
!| Description:
! export grid_integer to a ESRI ASCII file.
SUBROUTINE ExportGridIntegerToESRI_ASCII &
!
(layer, fileName)
USE Utilities, ONLY: &
!Imported routines:
GetUnit
IMPLICIT NONE
!Arguments with intent(in):
TYPE (grid_integer), INTENT (IN) :: layer
CHARACTER (LEN = *), INTENT (IN) :: fileName
!Local variables:
INTEGER (KIND = short) :: fileUnit
INTEGER (KIND = short) :: ios
INTEGER (KIND = short) :: i,j
!------------end of declaration------------------------------------------------
!open file
fileUnit = GetUnit ()
OPEN (UNIT = fileUnit, file = fileName, IOSTAT = ios)
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error opening file: ', &
code = openFileError, argument = fileName )
END IF
!write header
WRITE(fileUnit,'(a14,i4)') "ncols ", layer % jdim
WRITE(fileUnit,'(a14,i4)') "nrows ", layer % idim
WRITE(fileUnit,'(a14,f15.5)') "xllcorner ", layer % xllcorner
WRITE(fileUnit,'(a14,f15.5)') "yllcorner ", layer % yllcorner
WRITE(fileUnit,'(a14,f15.5)') "cellsize ", layer % cellsize
WRITE(fileUnit,'(a14,i8)') "NODATA_value ", layer % nodata
!write data
DO i = 1,layer % idim
DO j = 1, layer % jdim - 1
WRITE(fileUnit,'(i8," ")', ADVANCE = 'no') layer % mat(i,j)
END DO
WRITE(fileUnit,'(i8," ")') layer % mat(i,layer % jdim)
END DO
CLOSE (fileUnit)
END SUBROUTINE ExportGridIntegerToESRI_ASCII
!==============================================================================
!| Description:
! export grid_integer to a ESRI BINARY file.
SUBROUTINE ExportGridIntegerToESRI_BINARY &
!
(layer, fileName)
USE Utilities, ONLY: &
!Imported routines:
GetUnit
IMPLICIT NONE
!Arguments with intent(in):
TYPE (grid_integer), INTENT (IN) :: layer
CHARACTER (LEN = *), INTENT (IN) :: fileName
!Local variables:
INTEGER (KIND = short) :: fileUnit
INTEGER (KIND = short) :: ios
INTEGER (KIND = short) :: recordLength
INTEGER (KIND = short) :: recordNumber
INTEGER (KIND = short) :: i,j
!------------end of declaration------------------------------------------------
!open file
fileUnit = GetUnit ()
OPEN (UNIT = fileUnit, file = TRIM(fileName) // '.hdr', IOSTAT = ios)
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error opening file: ', &
code = openFileError, argument = TRIM(fileName) // '.hdr' )
END IF
!write header
WRITE(fileUnit,'(a14,i4)') "ncols ", layer % jdim
WRITE(fileUnit,'(a14,i4)') "nrows ", layer % idim
WRITE(fileUnit,'(a14,f15.5)') "xllcorner ", layer % xllcorner
WRITE(fileUnit,'(a14,f15.5)') "yllcorner ", layer % yllcorner
WRITE(fileUnit,'(a14,f15.5)') "cellsize ", layer % cellsize
WRITE(fileUnit,'(a14,i8)') "NODATA_value ", layer % nodata
WRITE(fileUnit,'(a18)') "BYTEORDER LSBFIRST"
CLOSE (fileUnit)
!write data
fileUnit = GetUnit ()
INQUIRE (IOLENGTH = recordLength) 100_long
OPEN (UNIT = fileUnit, file = TRIM(fileName) // '.flt', &
FORM = 'UNFORMATTED', ACCESS = 'DIRECT', RECL = recordLength, &
IOSTAT = ios)
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'error opening file: ', &
code = openFileError, argument = TRIM(fileName) // '.flt' )
END IF
recordNumber = 0
DO i = 1,layer % idim
DO j = 1, layer % jdim
recordNumber = recordNumber + 1
WRITE (fileUnit,REC = recordNumber) layer % mat (i,j)
END DO
END DO
CLOSE (fileUnit)
END SUBROUTINE ExportGridIntegerToESRI_BINARY
!==============================================================================
!| Description:
! read and calculate georeferencing informations from netCDF file.
SUBROUTINE GetGeoreferenceFromNCdataSet &
!
(ncId, varId, cellsize, xll, yll, grid_mapping, Iraster, Jraster, point2raster)
USE StringManipulation, ONLY: &
!Imported routines:
StringCompact, &
StringToLower
IMPLICIT NONE
! Arguments with intent (in):
INTEGER (KIND = short), INTENT(IN) :: ncId !!NetCdf Id for the file
INTEGER (KIND = short), INTENT(IN) :: varId !!variable Id
LOGICAL, INTENT(IN) :: point2raster
! Arguments with intent(out):
REAL (KIND = float), INTENT(OUT) :: cellsize !!cell resolution
REAL (KIND = float), INTENT(OUT) :: xll !!x coordinate of lower left corner of map
REAL (KIND = float), INTENT(OUT) :: yll !!y coordinate of lower left corner of map
TYPE (CRS), INTENT(OUT) :: grid_mapping !!coordinate reference system
! Arguments with intent(inout):
INTEGER (KIND = short), INTENT(INOUT) :: Iraster (:,:)
INTEGER (KIND = short), INTENT(INOUT) :: Jraster (:,:)
! Local variables:
INTEGER (KIND = short) :: x, y !!number of columns and rows
INTEGER (KIND = short) :: idx, idy
REAL (KIND = float), ALLOCATABLE :: xArray (:), yArray (:)
REAL (KIND = float), ALLOCATABLE :: xMatrix (:,:), yMatrix (:,:)
REAL (KIND = float), ALLOCATABLE :: xMatrix2 (:,:), yMatrix2 (:,:)
REAL (KIND = float) :: jdim, idim !! x and y resolution
REAL (KIND = float) :: xmin !! minimum value of x coordinate in xMatrix
REAL (KIND = float) :: xmax !! maximum value of x coordinate in xMatrix
REAL (KIND = float) :: ymin !! minimum value of y coordinate in yMatrix
REAL (KIND = float) :: ymax !! maximum value of y coordinate in yMatrix
INTEGER (KIND = short) :: ncStatus !!error code return by NetCDF routines
INTEGER (KIND = short) :: i, j
INTEGER (KIND = short) :: r, c
CHARACTER (LEN = 80) :: attribute
CHARACTER (LEN = 100) :: variableName
CHARACTER (LEN = 1) :: shp !!define if x and y are vectors or matrix
INTEGER (KIND = short) :: mappingId !!grid mapping variable Id
INTEGER (KIND = short) :: refSystem !!reference system
INTEGER (KIND = short) :: datum !!datum code
REAL (KIND = float) :: lat0 !!latitude of projection origin
REAL (KIND = float) :: centM !!longitude of central meridian
REAL (KIND = float) :: primeM !!longitude of prime meridian
REAL (KIND = float) :: falseE
REAL (KIND = float) :: falseN
REAL (KIND = float) :: scale_factor
REAL (KIND = float) :: tol = 0.01 !!tolerance for checking regularly spaced grid
INTEGER (KIND = short) :: latlon
!------------end of declaration------------------------------------------------
!get length of x and y sizes and related id
CALL GetXYSizesFromFile (ncId, x, y, idx = idx, idy = idy, shape = shp, latlon = latlon)
IF (shp == 'v' ) THEN !x and y values are stored in vectors
!allocate space to read coordinates
ALLOCATE ( xArray (x) )
ALLOCATE ( yArray (y) )
!read x coordinates
ncStatus = nf90_get_var (ncId, idx, xArray)
CALL ncErrorHandler (ncStatus)
!read y coordinates
ncStatus = nf90_get_var (ncId, idy, yArray)
CALL ncErrorHandler (ncStatus)
!calculate and check x spatial resolution
jdim = xArray (2) - xArray (1)
DO i = 3, x
IF ( ABS((xArray (i) - xArray (i-1) - jdim ) / jdim) > tol) THEN
CALL Catch ('error', 'GridLib', 'x dimension not regularly spaced' )
END IF
END DO
!calculate and check y spatial resolution
idim = yArray (2) - yArray (1)
DO i = 3, y
IF ( ABS((yArray (i) - yArray (i-1) - idim) / idim) > tol) THEN
CALL Catch ('error', 'GridLib', 'y dimension not regularly spaced' )
END IF
END DO
!set cellsize
IF (ABS((jdim - idim)/jdim) < tol) THEN
cellsize = (jdim + idim) / 2.
ELSE
CALL Catch ('error', 'GridLib', 'x resolution different from y resolution' )
END IF
!calculate xll and yll
xll = MINVAL (xArray) - cellsize / 2.
yll = MINVAL (yArray) - cellsize / 2.
!deallocate arrays
DEALLOCATE ( xArray )
DEALLOCATE ( yArray )
ELSE IF (shp == 'm') THEN !x and y values are stored in matrix
!allocate space to read coordinates
ALLOCATE ( xMatrix (x,y) )
ALLOCATE ( yMatrix (x,y) )
ALLOCATE ( xMatrix2 (y,x) )
ALLOCATE ( yMatrix2 (y,x) )
!read x coordinates
ncStatus = nf90_get_var (ncId, idx, xMatrix)
CALL ncErrorHandler (ncStatus)
!read y coordinates
ncStatus = nf90_get_var (ncId, idy, yMatrix)
CALL ncErrorHandler (ncStatus)
!calculate x spatial resolution
xmin = MINVAL(xMatrix)
xmax = MAXVAL(xMatrix)
jdim = (xmax - xmin) / (x - 1)
!calculate y spatial resolution
ymin = MINVAL(yMatrix)
ymax = MAXVAL(yMatrix)
idim = (ymax - ymin) / (y - 1)
!set cellsize
!cellsize = (idim + jdim) / 2.
cellsize = MAX(idim , jdim)
!calculate xll and yll
xll = xmin - cellsize / 2.
yll = ymin - cellsize / 2.
!swap x and y matrix
CALL SwapGridRealForward (xMatrix, xMatrix2, latlon)
CALL SwapGridRealForward (yMatrix, yMatrix2, latlon)
!build Iraster and Jraster matrix
IF (point2raster) THEN
Iraster = -9999
Jraster = -9999
DO i = 1, y
DO j = 1, x
c = INT ( (xMatrix2(i,j) - xll) / cellsize ) + 1
r = INT ( (yll + y * cellsize - yMatrix2(i,j)) / cellsize ) + 1
IF (r >= 1 .AND. r <= y .AND. c >= 1 .AND. c <= x ) THEN
Iraster(i,j) = r
Jraster(i,j) = c
END IF
END DO
END DO
END IF
!deallocate
DEALLOCATE (xMatrix)
DEALLOCATE (yMatrix)
DEALLOCATE (xMatrix2)
DEALLOCATE (yMatrix2)
END IF
!read coordinate reference system
attribute = ''
ncStatus = nf90_get_att (ncId, varid = varId, name = 'grid_mapping', &
values = attribute)
IF (ncStatus == nf90_noerr) THEN !check if grid_mapping exists
!retrieve varId of grid_mapping variable
ncStatus = nf90_inq_varid (ncId, attribute, mappingId)
CALL ncErrorHandler (ncStatus)
!retrieve datum EPSG code (extension to CF conventions)
ncStatus = nf90_get_att (ncId, varid = mappingId, name = 'datum_code', &
values = datum)
!if datum is not specified, datum_code does not exist, set default to WGS84
IF (ncStatus /= nf90_noerr) THEN
datum = WGS84
END IF
!retrieve grid mapping name
ncStatus = nf90_get_att (ncId, varid = mappingId, name = 'grid_mapping_name', &
values = attribute)
IF (ncStatus /= nf90_noerr) THEN !grid_mapping_name not found
!set default to geodetic WGS84
CALL SetCRS (GEODETIC, WGS84, grid_mapping)
CALL SetGeodeticParameters (grid_mapping, prime_meridian = 0.0)
ELSE
!retrieve reference system parameters
IF ( StringToLower (StringCompact (attribute) ) == 'transverse_mercator' ) THEN
CALL SetCRS (TM, datum, grid_mapping )
ncStatus = nf90_get_att (ncId, varid = mappingId, &
name = 'longitude_of_central_meridian', &
values = centM)
ncStatus = nf90_get_att (ncId, varid = mappingId, &
name = 'central_meridian', &
values = centM)
ncStatus = nf90_get_att (ncId, varid = mappingId, &
name = 'latitude_of_projection_origin', &
values = lat0)
ncStatus = nf90_get_att (ncId, varid = mappingId, &
name = 'scale_factor_at_central_meridian', &
values = scale_factor)
ncStatus = nf90_get_att (ncId, varid = mappingId, &
name = 'scale_factor', &
values = scale_factor)
ncStatus = nf90_get_att (ncId, varid = mappingId, &
name = 'false_easting', &
values = falseE)
ncStatus = nf90_get_att (ncId, varid = mappingId, &
name = 'false_northing', &
values = falseN)
CALL SetTransverseMercatorParameters (grid_mapping, lat0, centM, &
falseE, falseN, scale_factor)
ELSE IF ( StringCompact (attribute) == 'latitude_longitude' ) THEN
CALL SetCRS (GEODETIC, datum, grid_mapping )
ncStatus = nf90_get_att (ncId, varid = mappingId, &
name = 'longitude_of_prime_meridian', &
values = primeM)
CALL SetGeodeticParameters (grid_mapping, primeM)
ELSE
!other reference systems not yet supported
END IF
END IF
ELSE !CRS not specified: set default to geodetic WGS84
!retrieve name of variable
ncStatus = nf90_inquire_variable(ncId, varId, name = variableName)
CALL ncErrorHandler (ncStatus)
CALL Catch ('warning', 'GridLib', &
'grid_mapping not found in variable: ', &
argument = variableName )
CALL Catch ('info', 'GridLib', &
'set default coordinate reference system to geodetic wgs84')
CALL SetCRS (GEODETIC, WGS84, grid_mapping)
CALL SetGeodeticParameters (grid_mapping, prime_meridian = 0.0)
END IF
RETURN
END SUBROUTINE GetGeoreferenceFromNCdataSet
!==============================================================================
!| Description:
! extraxt the number of columns (x) and rows (y) from netCDF file.
SUBROUTINE GetXYSizesFromFile &
!
(ncId, x, y, idx, idy, shape, latlon)
IMPLICIT NONE
!Arguments with intent(in):
INTEGER (KIND = short), INTENT(IN) :: ncId !!NetCdf Id for the file
!Arguments with intent(out):
INTEGER (KIND = short), INTENT(OUT) :: x, y !!number of columns and rows
INTEGER (KIND = short), OPTIONAL, INTENT(OUT) :: idx !! id of x variable
INTEGER (KIND = short), OPTIONAL, INTENT(OUT) :: idy !! id of y variable
CHARACTER (LEN = *), INTENT(OUT) :: shape !!ccordinate in vector or matrix
INTEGER (KIND = short), OPTIONAL, INTENT(OUT) :: latlon !! coordinate order is lat-lon (1) or lon-lat (2)
! Local variables:
INTEGER (KIND = short) :: ncStatus !!error code return by NetCDF routines
INTEGER (KIND = short) :: nVars !!number of variables
INTEGER (KIND = short) :: IdXCoordinate !!Id of the variable containing
!!information on x ccordinate
INTEGER (KIND = short) :: IdYCoordinate !!Id of the variable containing
!!information on y ccordinate
INTEGER (KIND = short) :: IdXDim !!Id of the X dimension
INTEGER (KIND = short) :: IdYDim !!Id of the Y dimension
INTEGER (KIND = short) :: nDimsVar !!number of dimensions of a variable
INTEGER (KIND = short), ALLOCATABLE :: dimIds (:) !!dimension Ids of a variable
CHARACTER (LEN = 80) :: attribute
INTEGER (KIND = short) :: i
LOGICAL :: stdnamefound
LOGICAL :: longnamefound
LOGICAL :: unitsfound
!------------end of declaration------------------------------------------------
!inquire dataset to retrieve number of dimensions, variables
!and global attributes
ncStatus = nf90_inquire(ncId, nVariables = nVars )
CALL ncErrorHandler (ncStatus)
stdnamefound = .FALSE.
longnamefound = .FALSE.
unitsfound = .FALSE.
!scan dataset searching for variable referring to grid mapping
DO i = 1, nVars
attribute = ''
ncStatus = nf90_get_att (ncId, varid = i, name = 'standard_name', &
values = attribute)
IF (ncStatus == nf90_noerr) THEN !'standard_name' is found
IF ( attribute(1:23) == 'projection_x_coordinate' .OR. & !projected reference system
attribute(1:9) == 'longitude' .OR. & !geographic reference system
attribute(1:7) == 'Easting' .OR. &
attribute(1:14) == 'grid_longitude')THEN !rotated pole system
stdnamefound = .TRUE.
IdXCoordinate = i
ELSE IF ( attribute(1:23) == 'projection_y_coordinate' .OR. &
attribute(1:8) == 'latitude' .OR. & !geographic reference system
attribute(1:8) == 'Northing' .OR. &
attribute(1:13) == 'grid_latitude')THEN !rotated pole system
stdnamefound = .TRUE.
IdYCoordinate = i
END IF
END IF
END DO
IF ( .NOT. stdnamefound) THEN
CALL Catch ('warning', 'GridLib', &
'standard name not found while searching for x and y axis')
END IF
DO i = 1, nVars
attribute = ''
ncStatus = nf90_get_att (ncId, varid = i, name = 'long_name', &
values = attribute)
IF (ncStatus == nf90_noerr) THEN !'long_name' is found
IF ( attribute(1:23) == 'projection_x_coordinate' .OR. & !projected reference system
attribute(1:9) == 'longitude' .OR. & !geographic reference system
attribute(1:7) == 'Easting' .OR. &
attribute(1:14) == 'grid_longitude')THEN !rotated pole system
longnamefound = .TRUE.
IdXCoordinate = i
ELSE IF ( attribute(1:23) == 'projection_y_coordinate' .OR. &
attribute(1:8) == 'latitude' .OR. & !geographic reference system
attribute(1:8) == 'Northing' .OR. &
attribute(1:13) == 'grid_latitude')THEN !rotated pole system
longnamefound = .TRUE.
IdYCoordinate = i
END IF
END IF
END DO
IF ( .NOT. longnamefound) THEN
CALL Catch ('warning', 'GridLib', &
'long_name not found while searching for x and y axis')
END IF
DO i = 1, nVars
attribute = ''
ncStatus = nf90_get_att (ncId, varid = i, name = 'units', &
values = attribute)
IF (ncStatus == nf90_noerr) THEN !'units' is found
IF ( attribute(1:11) == 'degree_east' )THEN
unitsfound = .TRUE.
IdXCoordinate = i
ELSE IF ( attribute(1:12) == 'degree_north' )THEN
unitsfound = .TRUE.
IdYCoordinate = i
END IF
END IF
END DO
IF ( .NOT. unitsfound) THEN
CALL Catch ('warning', 'GridLib', &
'units not found while searching for x and y axis')
END IF
IF ( .NOT. stdnamefound .AND. &
.NOT. longnamefound .AND. &
.NOT. unitsfound ) THEN
CALL Catch ('error', 'GridLib', &
'x and y axis can not be found')
END IF
IF (PRESENT (idx) ) THEN
idx = IdXCoordinate
END IF
IF (PRESENT (idy) ) THEN
idy = IdYCoordinate
END IF
!retrieve Id of X dimension
ncStatus = nf90_inquire_variable (ncId, IdXCoordinate, ndims = nDimsVar)
CALL ncErrorHandler (ncStatus)
ALLOCATE (dimIds (nDimsVar))
ncStatus = nf90_inquire_variable (ncId, IdXCoordinate, dimids = dimIds)
CALL ncErrorHandler (ncStatus)
IF ( nDimsVar == 1) THEN !x is a vector
IdXDim = dimIds (1)
shape = 'v'
ELSE IF ( nDimsVar == 2) THEN
!x is a matrix. Used for not regular grids.
!Suppose that x is the first dimension
IdXDim = dimIds (1)
shape = 'm'
END IF
DEALLOCATE (dimIds)
!retrieve Id of Y dimension
ncStatus = nf90_inquire_variable (ncId, IdYCoordinate, ndims = nDimsVar)
CALL ncErrorHandler (ncStatus)
ALLOCATE (dimIds (nDimsVar))
ncStatus = nf90_inquire_variable (ncId, IdYCoordinate, dimids = dimIds)
CALL ncErrorHandler (ncStatus)
IF ( nDimsVar == 1) THEN !y is a vector
IdYDim = dimIds (1)
ELSE IF ( nDimsVar == 2) THEN
!y is a matrix. Used for not regular grids.
!Suppose that y is the second dimension
IdYDim = dimIds (2)
END IF
DEALLOCATE (dimIds)
!set coordinate order convention lon-lat or lat-lon
IF (PRESENT (latlon) ) THEN
IF (IdXDim > IdYDim) THEN !coordinate convention order is lon-lat
latlon = 1
!latlon = 2 !DAO
ELSE !coordinate convention order is lat-lon
latlon = 2
!latlon = 1 !DAO
END IF
END IF
!retrieve length
ncStatus = nf90_inquire_dimension (ncId, IdXDim, len = x)
CALL ncErrorHandler (ncStatus)
ncStatus = nf90_inquire_dimension (ncId, IdYDim, len = y)
CALL ncErrorHandler (ncStatus)
RETURN
END SUBROUTINE GetXYSizesFromFile
!==============================================================================
!| Description:
! Calculate the index to extract the corresponding slice from netcdf file.
FUNCTION TimeIndex &
!
(ncId, refTime, timeUnit, time) &
RESULT (index)
USE Units, ONLY: &
! Imported parameters:
minute, hour, day, month
USE StringManipulation, ONLY: &
!Imported routines:
StringtOsHORT
IMPLICIT NONE
! Arguments with intent(in):
INTEGER (KIND = short), INTENT(IN) :: ncId !!NetCdf Id for the file
TYPE (DateTime), INTENT(IN) :: refTime !!reference time to calculate time index
CHARACTER (LEN = *) :: timeUnit
TYPE (DateTime), INTENT (IN) :: time !!time to calculate index from reference time
! Local variables:
INTEGER (KIND = short) :: index
INTEGER (KIND = long) :: difference
INTEGER (KIND = short) :: ncStatus !!error code return by NetCDF routines
INTEGER (KIND = short) :: nDims !!number of dimensions
INTEGER (KIND = short) :: nVars !!number of variables
INTEGER (KIND = short) :: nAtts !!number of global attributes
INTEGER (KIND = short) :: length !!length of time dimension
INTEGER (KIND = short) :: idTime !!Id of the variable containing
!!information on time ccordinate
INTEGER (KIND = short) :: timeNumber
INTEGER (KIND = short) :: CurrentTimeNumber
CHARACTER (LEN = 80) :: attribute
CHARACTER (LEN = 100) :: variableName
INTEGER (KIND = short) :: i !!loop index
INTEGER :: slice (2)
INTEGER :: current
LOGICAL :: found
CHARACTER (LEN = 25) :: string
CHARACTER (LEN = 19) :: str
INTEGER, DIMENSION(NF90_MAX_VAR_DIMS) :: dimIDs
!------------end of declaration------------------------------------------------
!inquire dataset to retrieve number of dimensions, variables
!and global attributes
ncStatus = nf90_inquire(ncId, nDimensions = nDims, &
nVariables = nVars, &
nAttributes = nAtts )
CALL ncErrorHandler (ncStatus)
!search for time variable
DO i = 1, nVars
attribute = ''
ncStatus = nf90_get_att (ncId, varid = i, name = 'standard_name', &
values = attribute)
IF (ncStatus == nf90_noerr) THEN
IF ( attribute(1:4) == 'time' ) THEN
idTime = i
EXIT
END IF
ELSE !standard_name is not defined: search for variable named 'time'
!ncStatus = nf90_inq_varid (ncId, 'time', varid = i )
ncstatus = nf90_inquire_variable(ncId, varId = i, name = variableName)
IF (LEN_TRIM(variableName) == 4 .AND. &
variableName(1:4) == 'time' .OR. &
LEN_TRIM(variableName) == 4 .AND. &
variableName(1:4) == 'Time' .OR. &
LEN_TRIM(variableName) == 5 .AND. &
variableName(1:5) == 'Times' ) THEN !variable 'time' found
idTime = i
EXIT
END IF
END IF
END DO
!retrieve time length
length = GetTimeSteps (ncId)
!ncStatus = nf90_inquire_variable(ncid, idTime, dimids = dimIDs)
!CALL ncErrorHandler (ncStatus)
!
!ncStatus = nf90_inquire_dimension (ncId, dimid = dimIDs(2), len = length)
!CALL ncErrorHandler (ncStatus)
!search for current time
found = .FALSE.
IF (DateTimeIsDefault(refTime)) THEN
!build datetime string as used in netcdf file i.e 2007-10-11_00:00:00
timeString = time
!2000-01-01T00:00:00+00:00
timeString = timeString(1:10) // '_' // timeString(12:19)
!CurrentTimeNumber = StringToShort (timeString)
DO i = 1, length
slice(1) = 1
slice(2) = i
ncStatus = nf90_get_var (ncId, idTime, str , start = slice)
CALL ncErrorHandler (ncStatus)
IF (TRIM(str) == TRIM(timeString)) THEN
found = .TRUE.
index = i
EXIT
END IF
END DO
ELSE
!calculate time span in appropriate unit
difference = time - refTime
SELECT CASE (timeUnit)
CASE ('minutes')
difference = difference / INT(minute)
CASE ('hours')
difference = difference / INT(hour)
CASE ('days')
difference = difference / INT(day)
CASE ('months')
difference = difference / INT(month)
END SELECT
DO i = 1, length
slice(1) = i
ncStatus = nf90_get_var (ncId, idTime, current , start = slice)
CALL ncErrorHandler (ncStatus)
IF (current == difference) THEN
found = .TRUE.
index = i
EXIT
END IF
END DO
END IF
IF ( .NOT. found ) THEN
string = time
CALL Catch ('error', 'GridLib', &
'time not found in netcdf file: ', &
argument = string )
END IF
END FUNCTION TimeIndex
!==============================================================================
!| Description
! returns the time of the next grid in netcdf dataset
FUNCTION NextTime &
!
(ncId, refTime, timeUnit, current) &
!
RESULT (time)
USE Units, ONLY: &
! Imported parameters:
minute, hour, day, month
USE StringManipulation, ONLY: &
!Imported routines:
ToString
IMPLICIT NONE
! Arguments with intent(in):
INTEGER (KIND = short), INTENT(IN) :: ncId !!NetCdf Id for the file
TYPE (DateTime), INTENT(IN) :: refTime !!reference time to calculate time index
CHARACTER (LEN = *), INTENT(IN) :: timeUnit
INTEGER (KIND = short), INTENT(IN) :: current !!current time step
!Local variables:
TYPE (DateTime) :: time !!returned time of the next grid
INTEGER (KIND = short) :: next !!index of next time step
INTEGER (KIND = short) :: ncStatus !!error code return by NetCDF routines
INTEGER (KIND = short) :: nDims !!number of dimensions
INTEGER (KIND = short) :: nVars !!number of variables
INTEGER (KIND = short) :: nAtts !!number of global attributes
INTEGER (KIND = short) :: length !!length of time dimension
INTEGER (KIND = short) :: idTime !!Id of the variable containing
!!information on time ccordinate
CHARACTER (LEN = 80) :: attribute
CHARACTER (LEN = 100) :: variableName
INTEGER (KIND = short) :: i !!loop index
INTEGER :: slice (2)
INTEGER :: timeSpan
CHARACTER (LEN = 80) :: string
CHARACTER (LEN = 19) :: str
INTEGER, DIMENSION(NF90_MAX_VAR_DIMS) :: dimIDs
!------------end of declaration------------------------------------------------
!inquire dataset to retrieve number of dimensions, variables
!and global attributes
ncStatus = nf90_inquire(ncId, nDimensions = nDims, &
nVariables = nVars, &
nAttributes = nAtts )
CALL ncErrorHandler (ncStatus)
!search for time variable
DO i = 1, nVars
attribute = ''
ncStatus = nf90_get_att (ncId, varid = i, name = 'standard_name', &
values = attribute)
IF (ncStatus == nf90_noerr) THEN
IF ( attribute(1:4) == 'time' ) THEN
idTime = i
EXIT
END IF
ELSE !standard_name is not defined: search for variable named 'time'
!ncStatus = nf90_inq_varid (ncId, 'time', varid = i )
ncstatus = nf90_inquire_variable(ncId, varId = i, name = variableName)
IF (LEN_TRIM(variableName) == 4 .AND. &
variableName(1:4) == 'time' .OR. &
LEN_TRIM(variableName) == 4 .AND. &
variableName(1:4) == 'Time' .OR. &
LEN_TRIM(variableName) == 5 .AND. &
variableName(1:5) == 'Times') THEN !variable 'time' found
idTime = i
EXIT
END IF
END IF
END DO
!inquire time length
ncStatus = nf90_inquire_variable(ncid, idTime, dimids = dimIDs)
CALL ncErrorHandler (ncStatus)
!ncStatus = nf90_inquire_dimension (ncId, dimid = dimIDs(2), len = length)
ncStatus = nf90_inquire_dimension (ncId, dimid = dimIDs(1), len = length)
CALL ncErrorHandler (ncStatus)
!set next time step
IF (current < length) THEN
next = current + 1
ELSE
next = length
END IF
!compute date corresponding to next time step
!slice(1) = 1
!slice(2) = next
slice(1) = next
slice(2) = 1
IF (DateTimeIsDefault(refTime)) THEN
ncStatus = nf90_get_var (ncId, idTime, str , start = slice)
CALL ncErrorHandler (ncStatus)
string = str(1:10) // 'T' // str(12:19) // '+00:00'
time = string
ELSE
ncStatus = nf90_get_var (ncId, idTime, timeSpan , start = slice)
CALL ncErrorHandler (ncStatus)
SELECT CASE (timeUnit)
CASE ('minutes')
timeSpan = timeSpan * minute
CASE ('hours')
timeSpan = timeSpan * hour
CASE ('days')
timeSpan = timeSpan * day
CASE ('months')
timeSpan = timeSpan * month
END SELECT
time = refTime + timeSpan
END IF
RETURN
END FUNCTION NextTime
!==============================================================================
!| Description
! returns time given time index
FUNCTION TimeByIndex &
!
(ncId, refTime, timeUnit, index) &
!
RESULT (time)
USE Units, ONLY: &
! Imported parameters:
minute, hour, day, month
USE StringManipulation, ONLY: &
!Imported routines:
ToString
IMPLICIT NONE
! Arguments with intent(in):
INTEGER (KIND = short), INTENT(IN) :: ncId !!NetCdf Id for the file
TYPE (DateTime), INTENT(IN) :: refTime !!reference time to calculate time index
CHARACTER (LEN = *), INTENT(IN) :: timeUnit
INTEGER (KIND = short), INTENT(IN) :: index !! time step
!Local variables:
TYPE (DateTime) :: time !!returned time
INTEGER (KIND = short) :: ncStatus !!error code return by NetCDF routines
INTEGER (KIND = short) :: nDims !!number of dimensions
INTEGER (KIND = short) :: nVars !!number of variables
INTEGER (KIND = short) :: nAtts !!number of global attributes
INTEGER (KIND = short) :: length !!length of time dimension
INTEGER (KIND = short) :: idTime !!Id of the variable containing
!!information on time ccordinate
CHARACTER (LEN = 80) :: attribute
CHARACTER (LEN = 100) :: variableName
INTEGER (KIND = short) :: i !!loop index
INTEGER :: slice (2)
INTEGER :: timeSpan
CHARACTER (LEN = 80) :: string
CHARACTER (LEN = 19) :: str
INTEGER, DIMENSION(NF90_MAX_VAR_DIMS) :: dimIDs
!------------end of declaration------------------------------------------------
!inquire dataset to retrieve number of dimensions, variables
!and global attributes
ncStatus = nf90_inquire(ncId, nDimensions = nDims, &
nVariables = nVars, &
nAttributes = nAtts )
CALL ncErrorHandler (ncStatus)
!search for time variable
DO i = 1, nVars
attribute = ''
ncStatus = nf90_get_att (ncId, varid = i, name = 'standard_name', &
values = attribute)
IF (ncStatus == nf90_noerr) THEN
IF ( attribute(1:4) == 'time' ) THEN
idTime = i
EXIT
END IF
ELSE !standard_name is not defined: search for variable named 'time'
!ncStatus = nf90_inq_varid (ncId, 'time', varid = i )
ncstatus = nf90_inquire_variable(ncId, varId = i, name = variableName)
IF (LEN_TRIM(variableName) == 4 .AND. &
variableName(1:4) == 'time' .OR. &
LEN_TRIM(variableName) == 4 .AND. &
variableName(1:4) == 'Time' .OR. &
LEN_TRIM(variableName) == 5 .AND. &
variableName(1:5) == 'Times') THEN !variable 'time' found
idTime = i
EXIT
END IF
END IF
END DO
!inquire time length
ncStatus = nf90_inquire_variable(ncid, idTime, dimids = dimIDs)
CALL ncErrorHandler (ncStatus)
!ncStatus = nf90_inquire_dimension (ncId, dimid = dimIDs(2), len = length)
ncStatus = nf90_inquire_dimension (ncId, dimid = dimIDs(1), len = length)
CALL ncErrorHandler (ncStatus)
!set next time step
!IF (current < length) THEN
! next = current + 1
!ELSE
! next = length
!END IF
!compute date corresponding to next time step
!slice(1) = 1
!slice(2) = next
slice(1) = index
slice(2) = 1
IF (DateTimeIsDefault(refTime)) THEN
ncStatus = nf90_get_var (ncId, idTime, str , start = slice)
CALL ncErrorHandler (ncStatus)
string = str(1:10) // 'T' // str(12:19) // '+00:00'
time = string
ELSE
ncStatus = nf90_get_var (ncId, idTime, timeSpan , start = slice)
CALL ncErrorHandler (ncStatus)
SELECT CASE (timeUnit)
CASE ('minutes')
timeSpan = timeSpan * minute
CASE ('hours')
timeSpan = timeSpan * hour
CASE ('days')
timeSpan = timeSpan * day
CASE ('months')
timeSpan = timeSpan * month
END SELECT
time = refTime + timeSpan
END IF
RETURN
END FUNCTION TimeByIndex
!==============================================================================
!| Description:
! Parse units attribute of time variable
! Limits:
! string representing date and time must not contain blanks
SUBROUTINE ParseTime &
!
(ncId, ref_time, time_unit)
USE StringManipulation, ONLY: &
!Imported routines:
StringCompact, StringToShort
IMPLICIT NONE
! Arguments with intent(in):
INTEGER (KIND = short), INTENT(IN) :: ncId !!NetCdf Id for the file
! Arguments with intent(out):
TYPE(DateTime), INTENT(OUT) :: ref_time
CHARACTER (LEN = 7), INTENT(OUT) :: time_unit
! local variables:
INTEGER (KIND = short) :: ncStatus !!error code return by NetCDF routines
INTEGER (KIND = short) :: nDims !!number of dimensions
INTEGER (KIND = short) :: nVars !!number of variables
INTEGER (KIND = short) :: nAtts !!number of global attributes
INTEGER (KIND = short) :: idTime !!Id of the variable containing
!!information on time ccordinate
CHARACTER (LEN = 80) :: attribute
CHARACTER (LEN = 25) :: timeString
CHARACTER (LEN = 80) :: unit
CHARACTER (LEN = 10) :: since
CHARACTER (LEN = 100) :: variableName
LOGICAL :: error
CHARACTER (LEN = 4) :: YYYY !! year
CHARACTER (LEN = 2) :: MM !! month
CHARACTER (LEN = 2) :: DD !! day
CHARACTER (LEN = 2) :: hh !! hour
CHARACTER (LEN = 2) :: min !! minute
CHARACTER (LEN = 2) :: ss !! second
CHARACTER (LEN = 6) :: tz !! time zone
INTEGER (KIND = short) :: i !!loop index
!------------end of declaration------------------------------------------------
!inquire dataset to retrieve number of dimensions, variables
!and global attributes
ncStatus = nf90_inquire(ncId, nDimensions = nDims, &
nVariables = nVars, &
nAttributes = nAtts )
CALL ncErrorHandler (ncStatus)
idTime = 0
DO i = 1, nVars
attribute = ''
ncStatus = nf90_get_att (ncId, varid = i, name = 'standard_name', &
values = attribute)
IF (ncStatus == nf90_noerr) THEN !standard_name is defined
IF ( attribute(1:4) == 'time' ) THEN
idTime = i
EXIT
END IF
ELSE !standard_name is not defined: search for variable named 'time' or 'Times'
!ncStatus = nf90_inq_varid (ncId, 'time', varid = i )
ncstatus = nf90_inquire_variable(ncId, varId = i, name = variableName)
IF ( ( LEN_TRIM(variableName) == 4 .AND. &
variableName(1:4) == 'time' ) .OR. &
( LEN_TRIM(variableName) == 4 .AND. &
variableName(1:4) == 'Time' ) .OR. &
( LEN_TRIM(variableName) == 5 .AND. &
variableName(1:5) == 'Times' ) ) THEN !variable found
idTime = i
EXIT
END IF
END IF
END DO
!get units attribute
ncStatus = nf90_get_att (ncId, varid = idTime, name = 'units', &
values = attribute)
IF (ncStatus == nf90_noerr) THEN !units attribute found
READ(attribute,*) unit
i = INDEX (attribute, 'since', BACK = .TRUE.)
IF (i > 0 ) THEN !attribute contains 'since'
attribute = attribute ( i+6:LEN_TRIM(attribute) )
CALL ScanTimeStringAsString (attribute, YYYY, MM, DD, hh, min, ss, tz )
timeString = YYYY // '-' // MM // '-' // DD // 'T' // hh // ':' // min // ':' // ss // tz
!set reference time. Convert to UTC
ref_time = timeString
ref_time = ToUTC (ref_time)
ELSE !attribute is in the form like "day as %Y%m%d%h"
ref_time = timeDefault
END IF
!set time_unit
SELECT CASE (unit)
CASE ('seconds', 'second', 'sec', 's')
time_unit = 'seconds'
CASE ('minutes', 'minute', 'min')
time_unit = 'minutes'
CASE ('hours', 'hour', 'hr', 'h')
time_unit = 'hours'
CASE ('days', 'day', 'd')
time_unit = 'days'
CASE ('months', 'month')
time_unit = 'months'
END SELECT
ELSE
ref_time = timeDefault
END IF
RETURN
END SUBROUTINE
!==============================================================================
!| Description
! returns lower bounding time step of a given datetime
SUBROUTINE SyncTime &
!
(fileName, given, time)
USE Units, ONLY: &
! Imported parameters:
minute, hour, day, month
USE StringManipulation, ONLY: &
!Imported routines:
ToString
IMPLICIT NONE
! Arguments with intent(in):
CHARACTER (LEN = *), INTENT (IN) :: fileName
TYPE (DateTime), INTENT (IN) :: given
! Arguments with intent out
TYPE (DateTime), INTENT (OUT) :: time !!returned time of the grid to sync to
!Local variables:
INTEGER (KIND = short) :: ncId !!NetCdf Id for the file
TYPE (DateTime) :: refTime !!reference time to calculate time index
CHARACTER (LEN = 30) :: timeUnit
TYPE (DateTime) :: timeLower !!lower bound time
TYPE (DateTime) :: timeUpper !!upper bound time
!INTEGER (KIND = short) :: next !!index of next time step
INTEGER (KIND = short) :: ncStatus !!error code return by NetCDF routines
INTEGER (KIND = short) :: nDims !!number of dimensions
INTEGER (KIND = short) :: nVars !!number of variables
INTEGER (KIND = short) :: nAtts !!number of global attributes
INTEGER (KIND = short) :: length !!length of time dimension
INTEGER (KIND = short) :: idTime !!Id of the variable containing
!!information on time ccordinate
CHARACTER (LEN = 80) :: attribute
CHARACTER (LEN = 100) :: variableName
INTEGER (KIND = short) :: i !!loop index
INTEGER :: slice (2)
INTEGER :: timeSpan
CHARACTER (LEN = 80) :: string
CHARACTER (LEN = 19) :: str
INTEGER, DIMENSION(NF90_MAX_VAR_DIMS) :: dimIDs
!------------end of declaration------------------------------------------------
!open file
ncStatus = nf90_open (fileName, NF90_NOWRITE, ncId)
IF (ncStatus /= nf90_noerr) THEN
CALL Catch ('error', 'GridLib', &
TRIM (nf90_strerror (ncStatus) )//': ', &
code = ncIOError, argument = fileName )
ENDIF
!Read time information
CALL ParseTime (ncId, refTime, timeUnit)
!inquire dataset to retrieve number of dimensions, variables
!and global attributes
ncStatus = nf90_inquire(ncId, nDimensions = nDims, &
nVariables = nVars, &
nAttributes = nAtts )
CALL ncErrorHandler (ncStatus)
!search for time variable
DO i = 1, nVars
attribute = ''
ncStatus = nf90_get_att (ncId, varid = i, name = 'standard_name', &
values = attribute)
IF (ncStatus == nf90_noerr) THEN
IF ( attribute(1:4) == 'time' ) THEN
idTime = i
EXIT
END IF
ELSE !standard_name is not defined: search for variable named 'time'
!ncStatus = nf90_inq_varid (ncId, 'time', varid = i )
ncstatus = nf90_inquire_variable(ncId, varId = i, name = variableName)
IF (LEN_TRIM(variableName) == 4 .AND. &
variableName(1:4) == 'time' .OR. &
LEN_TRIM(variableName) == 5 .AND. &
variableName(1:5) == 'Times') THEN !variable 'time' found
idTime = i
EXIT
END IF
END IF
END DO
!inquire time length
ncStatus = nf90_inquire_variable(ncid, idTime, dimids = dimIDs)
CALL ncErrorHandler (ncStatus)
!ncStatus = nf90_inquire_dimension (ncId, dimid = dimIDs(2), len = length)
ncStatus = nf90_inquire_dimension (ncId, dimid = dimIDs(1), len = length)
CALL ncErrorHandler (ncStatus)
DO i = 1, length - 1
!set time step of lower bound
slice(1) = i
slice(2) = 1
!retrieve date and time
ncStatus = nf90_get_var (ncId, idTime, timeSpan , start = slice)
CALL ncErrorHandler (ncStatus)
SELECT CASE (timeUnit)
CASE ('minutes')
timeSpan = timeSpan * minute
CASE ('hours')
timeSpan = timeSpan * hour
CASE ('days')
timeSpan = timeSpan * day
CASE ('months')
timeSpan = timeSpan * month
END SELECT
timeLower = refTime + timeSpan
!set time step of upper bound
slice(1) = i + 1
slice(2) = 1
!retrieve date and time
ncStatus = nf90_get_var (ncId, idTime, timeSpan , start = slice)
CALL ncErrorHandler (ncStatus)
SELECT CASE (timeUnit)
CASE ('minutes')
timeSpan = timeSpan * minute
CASE ('hours')
timeSpan = timeSpan * hour
CASE ('days')
timeSpan = timeSpan * day
CASE ('months')
timeSpan = timeSpan * month
END SELECT
timeUpper = refTime + timeSpan
IF ( given == timeLower ) THEN
time = timeLower
RETURN
ELSE IF ( given == timeUpper ) THEN
time = timeUpper
RETURN
ELSE IF ( given > timeLower .AND. given < timeUpper ) THEN
time = timeLower
RETURN
END IF
END DO
!set next time step
!IF (current < length) THEN
! next = current + 1
!ELSE
! next = length
!END IF
!compute date corresponding to next time step
!slice(1) = 1
!slice(2) = next
!slice(1) = next
!slice(2) = 1
!IF (DateTimeIsDefault(refTime)) THEN
! ncStatus = nf90_get_var (ncId, idTime, str , start = slice)
! CALL ncErrorHandler (ncStatus)
! string = str(1:10) // 'T' // str(12:19) // '+00:00'
! time = string
!ELSE
! ncStatus = nf90_get_var (ncId, idTime, timeSpan , start = slice)
! CALL ncErrorHandler (ncStatus)
! SELECT CASE (timeUnit)
! CASE ('minutes')
! timeSpan = timeSpan * minute
! CASE ('hours')
! timeSpan = timeSpan * hour
! CASE ('days')
! timeSpan = timeSpan * day
! CASE ('months')
! timeSpan = timeSpan * month
! END SELECT
!
! time = refTime + timeSpan
!
!END IF
RETURN
END SUBROUTINE SyncTime
!==============================================================================
!| Description:
! get grid mapping for a floating point grid
FUNCTION GetGridMappingFloat &
!
( layer ) &
!
RESULT (grid_mapping)
IMPLICIT NONE
!Arguments with intent(in):
TYPE (grid_real), INTENT(INOUT) :: layer
!Local variables
TYPE (CRS) :: grid_mapping
!------------end of declaration------------------------------------------------
grid_mapping = layer % grid_mapping
END FUNCTION GetGridMappingFloat
!==============================================================================
!| Description:
! get grid mapping for a floating point grid
FUNCTION GetGridMappingInteger &
!
( layer ) &
!
RESULT (grid_mapping)
IMPLICIT NONE
!Arguments with intent(in):
TYPE (grid_integer), INTENT(INOUT) :: layer
!Local variables
TYPE (CRS) :: grid_mapping
!------------end of declaration------------------------------------------------
grid_mapping = layer % grid_mapping
END FUNCTION GetGridMappingInteger
!==============================================================================
!| Description:
! deallocate float grid
SUBROUTINE GridDestroyFloat &
!
(layer)
IMPLICIT NONE
! Arguments with intent(inout):
TYPE (grid_real), INTENT(INOUT) :: layer
! Local variables:
INTEGER (KIND = short) :: ios
!------------end of declaration------------------------------------------------
!deallocate data
IF ( ALLOCATED (layer % mat) ) THEN
DEALLOCATE ( layer % mat, STAT = ios )
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'memory deallocating grid float ' , &
code = memAllocError )
END IF
END IF
!deallocate coordinate reference system
IF ( ALLOCATED ( layer % grid_mapping % param) ) THEN
DEALLOCATE ( layer % grid_mapping % param, STAT = ios )
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'memory deallocating grid float ' , &
code = memAllocError )
END IF
END IF
IF ( ALLOCATED ( layer % grid_mapping % description) ) THEN
DEALLOCATE ( layer % grid_mapping % description, STAT = ios )
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'memory deallocating grid float ' , &
code = memAllocError )
END IF
END IF
END SUBROUTINE GridDestroyFloat
!==============================================================================
!| Description:
! deallocate integer grid
SUBROUTINE GridDestroyInteger &
!
(layer)
IMPLICIT NONE
! Arguments with intent(inout):
TYPE (grid_integer), INTENT(INOUT) :: layer
! Local variables:
INTEGER (KIND = short) :: ios
!------------end of declaration------------------------------------------------
IF ( ALLOCATED (layer % mat) ) THEN
DEALLOCATE ( layer % mat, STAT = ios )
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'memory deallocating grid integer ' , &
code = memAllocError )
END IF
END IF
!deallocate coordinate reference system
IF ( ALLOCATED ( layer % grid_mapping % param) ) THEN
DEALLOCATE ( layer % grid_mapping % param, STAT = ios )
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'memory deallocating grid float ' , &
code = memAllocError )
END IF
END IF
IF ( ALLOCATED ( layer % grid_mapping % description) ) THEN
DEALLOCATE ( layer % grid_mapping % description, STAT = ios )
IF (ios /= 0) THEN
CALL Catch ('error', 'GridLib', &
'memory deallocating grid float ' , &
code = memAllocError )
END IF
END IF
END SUBROUTINE GridDestroyInteger
!==============================================================================
!| Description:
! transport matrix from netcdf format to grid_real
SUBROUTINE SwapGridRealForward &
!
(matIn, matOut, latlon)
IMPLICIT NONE
!arguments with intent in:
REAL (KIND = float), INTENT (IN) :: matIn(:,:)
!arguments with intent out:
REAL (KIND = float), INTENT (OUT) :: matOut(:,:)
!local variables:
INTEGER :: i, j, idim, jdim, latlon
!----------------------end of declaration--------------------------------------
idim = SIZE (matOut,1)
jdim = SIZE (matOut,2)
DO i = 1, idim
DO j = 1, jdim
IF (latlon == 1) THEN
matOut (i,j) = matIn (j,idim - i + 1)
ELSE
matOut (i,j) = matIn (idim - i + 1,j)
END IF
END DO
END DO
END SUBROUTINE SwapGridRealForward
!==============================================================================
!| Description:
! transport matrix from netcdf format to grid_integer
SUBROUTINE SwapGridIntegerForward &
!
(matIn, matOut)
IMPLICIT NONE
!arguments with intent in:
INTEGER (KIND = long), INTENT (IN) :: matIn(:,:)
!arguments with intent out:
INTEGER (KIND = long), INTENT (OUT) :: matOut(:,:)
!local variables:
INTEGER :: i,j,idim,jdim
!----------------------end of declaration--------------------------------------
idim = SIZE (matOut,1)
jdim = SIZE (matOut,2)
DO i = 1, idim
DO j = 1, jdim
matOut (i,j) = matIn (j,idim - i + 1)
END DO
END DO
END SUBROUTINE SwapGridIntegerForward
!==============================================================================
!| Description:
! transport matrix from grid_real to netcdf format
SUBROUTINE SwapGridRealBack &
!
(matIn, matOut)
IMPLICIT NONE
!arguments with intent in:
REAL (KIND = float), INTENT (IN) :: matIn(:,:)
!arguments with intent out:
REAL (KIND = float), INTENT (OUT) :: matOut(:,:)
!local variables:
INTEGER :: i,j,idim,jdim
!----------------------end of declaration--------------------------------------
idim = SIZE (matOut,1)
jdim = SIZE (matOut,2)
DO i = 1, idim
DO j = 1, jdim
matOut (i,j) = matIn (jdim - j + 1,i)
END DO
END DO
END SUBROUTINE SwapGridRealBack
!==============================================================================
!| Description:
! transport matrix from grid_integer to netcdf format
SUBROUTINE SwapGridIntegerBack &
!
(matIn, matOut)
IMPLICIT NONE
!arguments with intent in:
INTEGER (KIND = long), INTENT (IN) :: matIn(:,:)
!arguments with intent out:
INTEGER (KIND = long), INTENT (OUT) :: matOut(:,:)
!local variables:
INTEGER :: i,j,idim,jdim
!----------------------end of declaration--------------------------------------
idim = SIZE (matOut,1)
jdim = SIZE (matOut,2)
DO i = 1, idim
DO j = 1, jdim
matOut (i,j) = matIn (jdim - j + 1,i)
END DO
END DO
END SUBROUTINE SwapGridIntegerBack
!==============================================================================
!| Description:
! Handle errors from netcdf related operation
SUBROUTINE ncErrorHandler &
!
(errcode)
IMPLICIT NONE
! Local scalars:
INTEGER (KIND = short), INTENT (IN) :: errcode
!------------end of declaration------------------------------------------------
IF (errcode /= nf90_noerr) THEN
CALL Catch ('error', 'GridLib', &
TRIM (nf90_strerror (errcode) ), &
code = ncIOError )
ENDIF
END SUBROUTINE ncErrorHandler
!! Description:
!! given filename of a multidimensional net-cdf file
!! the GetDtGrid function returns the temporal resolution (seconds)
!! assuming that it is regular
!! If option checkRegular is true the function check that
!! temporal resolution is regular
FUNCTION GetDtGrid &
!
(filename, checkRegular) &
!
RESULT (dt)
USE Units, ONLY: &
! Imported parameters:
minute, hour, day, month
USE StringManipulation, ONLY: &
! imported routines:
ToString
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(IN) :: filename
LOGICAL, OPTIONAL, INTENT(IN) :: checkRegular
!Local declarations:
INTEGER (KIND = long) :: dt
INTEGER (KIND = short) :: ncStatus !!error code returned by NetCDF routines
INTEGER (KIND = short) :: ncId !!NetCdf Id for the file
INTEGER (KIND = short) :: nDims !!number of dimensions
INTEGER (KIND = short) :: nVars !!number of variables
INTEGER (KIND = short) :: nAtts !!number of global attributes
INTEGER (KIND = short) :: timeDimId !!id of time dimension
INTEGER (KIND = short) :: length !!length of time dimension
INTEGER (KIND = short) :: idTime !!Id of the variable containing
!!information on time coordinate
CHARACTER (LEN = 80) :: attribute
CHARACTER (LEN = 25) :: timeString
CHARACTER (LEN = 100) :: variableName
TYPE(DateTime) :: ref_time
TYPE(DateTime) :: date1, date2
CHARACTER (LEN = 7) :: time_unit
INTEGER :: slice (1)
INTEGER :: slice2 (2)
INTEGER :: time1, time2
INTEGER :: timeSpan
INTEGER (KIND = short) :: i
INTEGER, DIMENSION(NF90_MAX_VAR_DIMS) :: dimIDs
CHARACTER (LEN = 19) :: str, str1, str2
!------------end of declaration------------------------------------------------
!open net-cdf file with read-only access
ncStatus = nf90_open (fileName, NF90_NOWRITE, ncId)
IF (ncStatus /= nf90_noerr) THEN
CALL Catch ('error', 'GridLib', &
TRIM (nf90_strerror (ncStatus) )//': ', &
code = ncIOError, argument = fileName )
ENDIF
!retrieve time unit
CALL ParseTime (ncId, ref_time, time_unit)
!inquire dataset to retrieve number of dimensions, variables
!and global attributes
ncStatus = nf90_inquire(ncId, nDimensions = nDims, &
nVariables = nVars, &
nAttributes = nAtts )
CALL ncErrorHandler (ncStatus)
!search for time variable
DO i = 1, nVars
attribute = ''
ncStatus = nf90_get_att (ncId, varid = i, name = 'standard_name', &
values = attribute)
IF (ncStatus == nf90_noerr) THEN !standard_name is defined
IF ( attribute(1:4) == 'time' ) THEN
idTime = i
EXIT
END IF
ELSE !standard_name is not defined: search for variable named 'time'
!ncStatus = nf90_inq_varid (ncId, 'time', varid = i )
ncstatus = nf90_inquire_variable(ncId, varId = i, name = variableName)
IF (LEN_TRIM(variableName) == 4 .AND. &
variableName(1:4) == 'time' .OR. &
LEN_TRIM(variableName) == 5 .AND. &
variableName(1:5) == 'Times' ) THEN !variable 'time' found
idTime = i
EXIT
END IF
END IF
END DO
!retrieve time length
length = GetTimeSteps (ncId)
!compute dt
IF (DateTimeIsDefault(ref_time)) THEN
slice2(1) = 1
slice2(2) = 1
ncStatus = nf90_get_var (ncId, idTime, str , start = slice2)
CALL ncErrorHandler (ncStatus)
!build datetime string from format used in netcdf file i.e 2007-10-11_00:00:00
timeString = str(1:10) // 'T' // str(12:19) // '+00:00'
date1 = timeString
slice2(1) = 1
slice2(2) = 2
ncStatus = nf90_get_var (ncId, idTime, str , start = slice2)
CALL ncErrorHandler (ncStatus)
!build datetime string from format used in netcdf file i.e 2007-10-11_00:00:00
timeString = str(1:10) // 'T' // str(12:19) // '+00:00'
date2 = timeString
dt = date2 - date1
ELSE
slice(1) = 1
ncStatus = nf90_get_var (ncId, idTime, time1 , start = slice)
CALL ncErrorHandler (ncStatus)
slice(1) = 2
ncStatus = nf90_get_var (ncId, idTime, time2 , start = slice)
CALL ncErrorHandler (ncStatus)
dt = time2 - time1
!convert in seconds
SELECT CASE (time_unit)
CASE ('minutes')
dt = dt * minute
CASE ('hours')
dt = dt * hour
CASE ('days')
dt = dt * day
CASE ('months')
dt = dt * month
END SELECT
END IF
!Check if dt is regular
IF (PRESENT (checkRegular) ) THEN
IF (checkRegular) THEN
DO i = 1, length - 1
IF (DateTimeIsDefault(ref_time)) THEN
slice2(1) = 1
slice2(2) = i
ncStatus = nf90_get_var (ncId, idTime, str1 , start = slice2)
CALL ncErrorHandler (ncStatus)
slice2(2) = i + 1
ncStatus = nf90_get_var (ncId, idTime, str2 , start = slice2)
CALL ncErrorHandler (ncStatus)
timeString = str1(1:10) // 'T' // str1(12:19) // '+00:00'
date1 = timeString
timeString = str2(1:10) // 'T' // str2(12:19) // '+00:00'
date2 = timeString
timeSpan = date2 - date1
ELSE
slice(1) = i
ncStatus = nf90_get_var (ncId, idTime, time1 , start = slice)
CALL ncErrorHandler (ncStatus)
slice(1) = i + 1
ncStatus = nf90_get_var (ncId, idTime, time2 , start = slice)
CALL ncErrorHandler (ncStatus)
IF (DateTimeIsDefault(ref_time)) THEN
timeString = ToString (time1)
timeString = timeString (1:4) // '-' // &
timeString (5:6) // '-' // &
timeString (7:8) // 'T' // &
timeString (9:10) // ':00:00+00:00'
date1 = timeString
timeString = ToString (time2)
timeString = timeString (1:4) // '-' // &
timeString (5:6) // '-' // &
timeString (7:8) // 'T' // &
timeString (9:10) // ':00:00+00:00'
date2 = timeString
timeSpan = date2 - date1
ELSE
timeSpan = time2 - time1
!convert in seconds
SELECT CASE (time_unit)
CASE ('minutes')
timeSpan = timeSpan * minute
CASE ('hours')
timeSpan = timeSpan * hour
CASE ('days')
timeSpan = timeSpan * day
CASE ('months')
timeSpan = timeSpan * month
END SELECT
END IF
END IF
IF (timeSpan /= dt ) THEN
CALL Catch ('error', 'GridLib', &
'time not regular in multidimensional grid')
END IF
END DO
END IF
END IF
END FUNCTION GetDtGrid
!==============================================================================
!| Description:
! given filename of a multidimensional net-cdf file
! the GetFirstDate function returns the date and time
! of first grid
FUNCTION GetFirstDate &
!
(filename) &
!
RESULT (time)
USE Chronos, ONLY: &
!Imported type definitions:
DateTime
USE Units, ONLY: &
! Imported parameters:
minute, hour, day, month
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(IN) :: filename
!Local declarations:
TYPE (DateTime) :: time
INTEGER (KIND = short) :: ncStatus !!error code returned by NetCDF routines
INTEGER (KIND = short) :: ncId !!NetCdf Id for the file
TYPE(DateTime) :: ref_time
CHARACTER (LEN = 7) :: time_unit
INTEGER (KIND = short) :: idTime !!Id of the variable containing
!!information on time coordinate
INTEGER (KIND = short) :: nDims !!number of dimensions
INTEGER (KIND = short) :: nVars !!number of variables
INTEGER (KIND = short) :: nAtts !!number of global attributes
CHARACTER (LEN = 80) :: attribute
CHARACTER (LEN = 100) :: variableName
INTEGER :: slice (1)
INTEGER :: slice2 (2)
INTEGER :: time1
INTEGER :: dt
INTEGER (KIND = short) :: i
CHARACTER (LEN = 19) :: str
!------------end of declaration------------------------------------------------
!open net-cdf file with read-only access
ncStatus = nf90_open (fileName, NF90_NOWRITE, ncId)
IF (ncStatus /= nf90_noerr) THEN
CALL Catch ('error', 'GridLib', &
TRIM (nf90_strerror (ncStatus) )//': ', &
code = ncIOError, argument = fileName )
ENDIF
!retrieve time unit
CALL ParseTime (ncId, ref_time, time_unit)
!inquire dataset to retrieve number of dimensions, variables
!and global attributes
ncStatus = nf90_inquire(ncId, nDimensions = nDims, &
nVariables = nVars, &
nAttributes = nAtts )
CALL ncErrorHandler (ncStatus)
!search for time variable
DO i = 1, nVars
attribute = ''
ncStatus = nf90_get_att (ncId, varid = i, name = 'standard_name', &
values = attribute)
IF (ncStatus == nf90_noerr) THEN !standard_name is defined
IF ( attribute(1:4) == 'time' ) THEN
idTime = i
EXIT
END IF
ELSE !standard_name is not defined: search for variable named 'time'
!ncStatus = nf90_inq_varid (ncId, 'time', varid = i )
ncstatus = nf90_inquire_variable(ncId, varId = i, name = variableName)
IF (LEN_TRIM(variableName) == 4 .AND. &
variableName(1:4) == 'time' .OR. &
LEN_TRIM(variableName) == 5 .AND. &
variableName(1:5) == 'Times' ) THEN !variable 'time' found
idTime = i
EXIT
END IF
END IF
END DO
IF (DateTimeIsDefault(ref_time)) THEN
slice2(1) = 1
slice2(2) = 1
ncStatus = nf90_get_var (ncId, idTime, str , start = slice2)
CALL ncErrorHandler (ncStatus)
!build datetime string from format used in netcdf file i.e 2007-10-11_00:00:00
timeString = str(1:10) // 'T' // str(12:19) // '+00:00'
time = timeString
ELSE
!retrieve timespan of first grid
slice(1) = 1
ncStatus = nf90_get_var (ncId, idTime, time1 , start = slice)
CALL ncErrorHandler (ncStatus)
dt = time1
!convert in seconds
SELECT CASE (time_unit)
CASE ('minutes')
dt = dt * minute
CASE ('hours')
dt = dt * hour
CASE ('days')
dt = dt * day
CASE ('months')
dt = dt * month
END SELECT
!compute time
time = ref_time + dt
END IF
RETURN
END FUNCTION GetFirstDate
!==============================================================================
!| Description:
! given filename of a multidimensional net-cdf file
! the GetTimeStepsFromFile function returns the number of time steps
FUNCTION GetTimeStepsFromFile &
!
(filename) &
!
RESULT (steps)
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT(IN) :: filename
!Local declarations:
INTEGER (KIND = short) :: steps
INTEGER (KIND = short) :: ncStatus !!error code returned by NetCDF routines
INTEGER (KIND = short) :: ncId !!NetCdf Id for the file
INTEGER (KIND = short) :: nDims !!number of dimensions
INTEGER (KIND = short) :: nDimsVar !!number of dimensions of a variable
INTEGER (KIND = short) :: nVars !!number of variables
INTEGER (KIND = short) :: nAtts !!number of global attributes
INTEGER (KIND = short) :: idTime !!Id of the variable containing
!!information on time coordinate
INTEGER (KIND = short) :: dimId !dimension id
CHARACTER (LEN = 80) :: attribute
CHARACTER (LEN = 100) :: variableName
INTEGER (KIND = short) :: i
INTEGER, DIMENSION(NF90_MAX_VAR_DIMS) :: dimIDs
!------------end of declaration------------------------------------------------
!open net-cdf file with read-only access
ncStatus = nf90_open (fileName, NF90_NOWRITE, ncId)
IF (ncStatus /= nf90_noerr) THEN
CALL Catch ('error', 'GridLib', &
TRIM (nf90_strerror (ncStatus) )//': ', &
code = ncIOError, argument = fileName )
ENDIF
!search for "Time" dimension
ncStatus = nf90_inq_dimid(ncid, "Time", dimId)
IF (ncStatus == nf90_noerr) THEN !Time dimension found
ncStatus = nf90_inquire_dimension (ncId, dimId, len = steps)
RETURN !no need to go on
END IF
!search for "time" dimension
ncStatus = nf90_inq_dimid(ncid, "time", dimId)
IF (ncStatus /= nf90_noerr) THEN !time dimension found
ncStatus = nf90_inquire_dimension (ncId, dimId, len = steps)
RETURN !no need to go on
END IF
!If dimension has a different name, analyse variable
!inquire dataset to retrieve number of dimensions, variables
!and global attributes
ncStatus = nf90_inquire(ncId, nDimensions = nDims, &
nVariables = nVars, &
nAttributes = nAtts )
CALL ncErrorHandler (ncStatus)
!search for time variable
DO i = 1, nVars
attribute = ''
ncStatus = nf90_get_att (ncId, varid = i, name = 'standard_name', &
values = attribute)
IF (ncStatus == nf90_noerr) THEN !standard_name is defined
IF ( attribute(1:4) == 'time' ) THEN
idTime = i
EXIT
END IF
ELSE !standard_name is not defined: search for variable named 'time'
!ncStatus = nf90_inq_varid (ncId, 'time', varid = i )
ncstatus = nf90_inquire_variable(ncId, varId = i, name = variableName)
IF (LEN_TRIM(variableName) == 4 .AND. &
variableName(1:4) == 'time' .OR. &
LEN_TRIM(variableName) == 5 .AND. &
variableName(1:5) == 'Times' ) THEN !variable 'time' found
idTime = i
EXIT
END IF
END IF
END DO
!retrieve dimensions of variable
ncStatus = nf90_inquire_variable (ncId, idTime, ndims = nDimsVar)
CALL ncErrorHandler (ncStatus)
!retrieve dimension id of variable
ncStatus = nf90_inquire_variable(ncid, idTime, dimids = dimIDs)
CALL ncErrorHandler (ncStatus)
!inquire time length
IF (nDimsVar == 1) THEN
ncStatus = nf90_inquire_dimension (ncId, dimid = dimIDs(1), len = steps)
CALL ncErrorHandler (ncStatus)
ELSE IF (nDimsVar == 2) THEN
ncStatus = nf90_inquire_dimension (ncId, dimid = dimIDs(2), len = steps)
CALL ncErrorHandler (ncStatus)
END IF
RETURN
END FUNCTION GetTimeStepsFromFile
!==============================================================================
!| Description:
! given ncId of a multidimensional net-cdf file
! the GetTimeStepsFromNCid function returns the number of time steps
FUNCTION GetTimeStepsFromNCid &
!
(ncId) &
!
RESULT (steps)
IMPLICIT NONE
!Arguments with intent(in):
INTEGER (KIND = short), INTENT(IN) :: ncId !!NetCdf Id for the file
!Local declarations:
INTEGER (KIND = short) :: steps
INTEGER (KIND = short) :: ncStatus !!error code returned by NetCDF routines
INTEGER (KIND = short) :: nDims !!number of dimensions
INTEGER (KIND = short) :: nDimsVar !!number of dimensions of a variable
INTEGER (KIND = short) :: nVars !!number of variables
INTEGER (KIND = short) :: nAtts !!number of global attributes
INTEGER (KIND = short) :: idTime !!Id of the variable containing
!!information on time coordinate
INTEGER (KIND = short) :: dimId !dimension id
CHARACTER (LEN = 80) :: attribute
CHARACTER (LEN = 100) :: variableName
INTEGER (KIND = short) :: i
INTEGER, DIMENSION(NF90_MAX_VAR_DIMS) :: dimIDs
!------------end of declaration------------------------------------------------
!search for "Time" dimension
ncStatus = nf90_inq_dimid(ncid, "Time", dimId)
IF (ncStatus == nf90_noerr) THEN !Time dimension found
ncStatus = nf90_inquire_dimension (ncId, dimId, len = steps)
RETURN !no need to go on
END IF
!search for "time" dimension
ncStatus = nf90_inq_dimid(ncid, "time", dimId)
IF (ncStatus /= nf90_noerr) THEN !time dimension found
ncStatus = nf90_inquire_dimension (ncId, dimId, len = steps)
RETURN !no need to go on
END IF
!If dimension has a different name, analyse variable
!inquire dataset to retrieve number of dimensions, variables
!and global attributes
ncStatus = nf90_inquire(ncId, nDimensions = nDims, &
nVariables = nVars, &
nAttributes = nAtts )
CALL ncErrorHandler (ncStatus)
!search for time variable
DO i = 1, nVars
attribute = ''
ncStatus = nf90_get_att (ncId, varid = i, name = 'standard_name', &
values = attribute)
IF (ncStatus == nf90_noerr) THEN !standard_name is defined
IF ( attribute(1:4) == 'time' ) THEN
idTime = i
EXIT
END IF
ELSE !standard_name is not defined: search for variable named 'time'
!ncStatus = nf90_inq_varid (ncId, 'time', varid = i )
ncstatus = nf90_inquire_variable(ncId, varId = i, name = variableName)
IF (LEN_TRIM(variableName) == 4 .AND. &
variableName(1:4) == 'time' .OR. &
LEN_TRIM(variableName) == 4 .AND. &
variableName(1:4) == 'Time' .OR. &
LEN_TRIM(variableName) == 5 .AND. &
variableName(1:5) == 'Times' ) THEN !variable 'time' found
idTime = i
EXIT
END IF
END IF
END DO
!retrieve dimensions of variable
ncStatus = nf90_inquire_variable (ncId, idTime, ndims = nDimsVar)
CALL ncErrorHandler (ncStatus)
!retrieve dimension id of variable
ncStatus = nf90_inquire_variable(ncid, idTime, dimids = dimIDs)
CALL ncErrorHandler (ncStatus)
!inquire time length
IF (nDimsVar == 1) THEN
ncStatus = nf90_inquire_dimension (ncId, dimid = dimIDs(1), len = steps)
CALL ncErrorHandler (ncStatus)
ELSE IF (nDimsVar == 2) THEN
ncStatus = nf90_inquire_dimension (ncId, dimid = dimIDs(2), len = steps)
CALL ncErrorHandler (ncStatus)
END IF
RETURN
END FUNCTION GetTimeStepsFromNCid
!==============================================================================
!| Description:
! scan a string to extract time information as character strings:
! year, month, day, hour, minute, second, timezone
! supported formats:
!
! * `1900-01-01T01:00:00+00:00`
! * `1900-1-1 01:00:00`
! * `1900-1-1 1:0:0`
!
SUBROUTINE ScanTimeStringAsString &
!
(string, YYYY, MM, DD, hh, min, ss, tz )
USE StringManipulation, ONLY: &
!Imported routines:
StringTokenize, StringCompact, &
StringToShort, ToString
IMPLICIT NONE
!Arguments with intent(in):
CHARACTER (LEN = *), INTENT (IN) :: string
CHARACTER (LEN = *), INTENT (OUT) :: YYYY !! year
CHARACTER (LEN = *), INTENT (OUT) :: MM !! month
CHARACTER (LEN = *), INTENT (OUT) :: DD !! day
CHARACTER (LEN = *), INTENT (OUT) :: hh !! hour
CHARACTER (LEN = *), INTENT (OUT) :: min !! minute
CHARACTER (LEN = *), INTENT (OUT) :: ss !! second
CHARACTER (LEN = *), INTENT (OUT) :: tz !!time zone
!Local declarations:
CHARACTER (LEN = 2) :: tzhh !! time zone hour
CHARACTER (LEN = 2) :: tzmin !! time zone minute
INTEGER (KIND = short) :: i, iplus, iminus
INTEGER (KIND = short) :: year, month, day, hour, minute, second, tzhour, tzminute
CHARACTER (len=100), POINTER :: args (:)
CHARACTER (LEN = 20) :: time, tzstring
INTEGER (KIND = short) :: nargs
character(len=1024) :: format_string
!------------end of declaration------------------------------------------------
!search for year, month, and day
CALL StringTokenize (string = string, delims = '-', args = args, nargs = nargs)
year = StringToShort ( StringCompact (args (1)) )
month = StringToShort ( StringCompact (args (2)) )
!search for 'T' after day
i = INDEX (args (3), 'T', BACK = .TRUE.)
IF (i > 0) THEN
day = StringToShort ( args (3) (1 : i-1) )
ELSE
day = StringToShort ( StringCompact (args (3)) )
END IF
!search for time: hour, minute, second
!check wether T is present
IF ( INDEX (string, 'T') > 0) THEN
i = INDEX (string, 'T')
ELSE IF ( INDEX (string, ' ') > 0) THEN
i = INDEX (string, ' ')
END IF
time = string ( i+1 : len_trim (string) )
CALL StringTokenize (string = time, delims = ':', args = args, nargs = nargs)
hour = StringToShort ( StringCompact (args (1)) )
minute = StringToShort ( StringCompact (args (2)) )
iplus = INDEX (args(3), '+')
iminus = INDEX (args(3), '-')
format_string = "(I2.2)"
IF ( iplus > 0) THEN
second = StringToShort ( StringCompact (args (3) (1 : iplus - 1) ) )
CALL StringTokenize (string = time, delims = '+', args = args, nargs = nargs)
tzstring = args (2)
CALL StringTokenize (string = tzstring, delims = ':', args = args, nargs = nargs)
tzhour = StringToShort ( StringCompact (args (1)) )
tzminute = StringToShort ( StringCompact (args (2)) )
tzhh = ToString (tzhour, format_string)
tzmin = ToString (tzminute, format_string)
tz = '+' // tzhh // ':' // tzmin
ELSE IF (iminus > 0 ) THEN
second = StringToShort ( StringCompact (args (3) (1 : iminus - 1) ) )
CALL StringTokenize (string = time, delims = '-', args = args, nargs = nargs)
tzstring = args (2)
CALL StringTokenize (string = tzstring, delims = ':', args = args, nargs = nargs)
tzhour = StringToShort ( StringCompact (args (1)) )
tzminute = StringToShort ( StringCompact (args (2)) )
tzhh = ToString (tzhour, format_string)
tzmin = ToString (tzminute, format_string)
tz = '-' // tzhh // ':' // tzmin
ELSE
second = StringToShort ( StringCompact (args (3) ) )
!time zone is not given, set to default
tzhh = '00'
tzmin = '00'
tz = '+' // tzhh // ':' // tzmin
END IF
!convert short integer to string
YYYY = ToString (year)
MM = ToString (month, format_string)
DD = ToString (day, format_string)
hh = ToString (hour, format_string)
min = ToString (minute, format_string)
ss = ToString (second, format_string)
RETURN
END SUBROUTINE ScanTimeStringAsString
END MODULE GridLib