; docformat = 'rst'
;
; NAME:
;   cgFINDMAPBOUNDARY
;
; PURPOSE:
; 
; Utility routine to find the map projection grid boundary from a file,
; if it is possible to do so. Currently works with GeoTIFF files, CF 1.4
; compliant netCDF files, and GPD files created with the GPD_Viewer software
; from the Catatlyst Library.
;******************************************************************************************;
;                                                                                          ;
;  Copyright (c) 2012, by Fanning Software Consulting, Inc. All rights reserved.           ;
;                                                                                          ;
;  Redistribution and use in source and binary forms, with or without                      ;
;  modification, are permitted provided that the following conditions are met:             ;
;                                                                                          ;
;      * Redistributions of source code must retain the above copyright                    ;
;        notice, this list of conditions and the following disclaimer.                     ;
;      * Redistributions in binary form must reproduce the above copyright                 ;
;        notice, this list of conditions and the following disclaimer in the               ;
;        documentation and/or other materials provided with the distribution.              ;
;      * Neither the name of Fanning Software Consulting, Inc. nor the names of its        ;
;        contributors may be used to endorse or promote products derived from this         ;
;        software without specific prior written permission.                               ;
;                                                                                          ;
;  THIS SOFTWARE IS PROVIDED BY FANNING SOFTWARE CONSULTING, INC. ''AS IS'' AND ANY        ;
;  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES    ;
;  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT     ;
;  SHALL FANNING SOFTWARE CONSULTING, INC. BE LIABLE FOR ANY DIRECT, INDIRECT,             ;
;  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED    ;
;  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;         ;
;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND             ;
;  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT              ;
;  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS           ;
;  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                            ;
;******************************************************************************************;
;
;+
; Utility routine to find the map projection grid boundary from a file,
; if it is possible to do so. Currently works with GeoTIFF files, CF 1.4
; compliant netCDF files, and GPD files created with the GPD_Viewer software
; from the Catatlyst Library.
;
; :Categories:
;    Utility
; 
; :Returns:
;    The return value is either a 1, indicating a boundary can be found, or a 0 indicating
;    that the boundary could not be found.
;    
; :Params:
;    filename: in, required, type='string'
;       The name of a filename to open to see if a projected map grid boundary can be found.
;    boundary: out, optional, type=float
;       The boundary of the image in projected meter space, in the form [x0,y0,x1,y1].
;       
; :Keywords:
;    use_latlon:  in, optional, type=boolean, default=0
;        If the filename is a netCDF file, set this keyword to force the boundary 
;        to be determined by reading the include latitude/longitude arrays.
;    utm_south: in, optional, type=boolean, default=0
;        Set this keyword to add 10e6 to each of the Y values. This is sometimes
;        necessary with LandSat images in the Southern hemisphere, where the Y values
;        are given in negative values to indicate southern UTM zones.
;    xrange: out, optional, type=float
;        A two element vector: boundary[[0,2]]
;    yrange: out, optional, type=float
;        A two element vector: boundary[[1,3]]
;
; :Author:
;    FANNING SOFTWARE CONSULTING::
;       David W. Fanning 
;       1645 Sheely Drive
;       Fort Collins, CO 80526 USA
;       Phone: 970-221-0438
;       E-mail: david@idlcoyote.com
;       Coyote's Guide to IDL Programming: http://www.idlcoyote.com
;
; :History:
;    Written by: David W. Fanning, 23 February 2010.::
;       Added XRANGE and YRANGE keywords. 25 February 2010. DWF
;       Added USE_LATLON keyword for finding boundary of netCDF files containing latitude
;          and longitude arrays. 6 April 2010. DWF.
;       Added UTM_SOUTH keyword to handle Landsat dat in UTM projections in GeoTiff files
;          that have to have 10e6 added to Y values to make them work in IDL. 14 Aug 2012. DWF.
;       Renamed cgFindMapBoundary from FindMapBoundary. 21 Aug 2012. DWF.
;       Changed reference to NCDF_Coord to cgNCDFMap. 28 Oct 2012. DWF.
;          
; :Copyright:
;     Copyright (c) 2012, Fanning Software Consulting, Inc.
;-
FUNCTION cgFindMapBoundary, filename, boundary, $
    USE_LATLON=use_latlon, $
    UTM_SOUTH=utm_south, $
    XRANGE=xrange, $
    YRANGE=yrange

    COMPILE_OPT idl2

    On_Error, 2
        
    ; Is this a GeoTIFF file:
    isGeoTiff = Query_Tiff(filename, geoInfo, GEOTIFF=geoStruct)

    ; If it is a GeoTiff file, do this.
    IF isGeoTiff THEN BEGIN
        
        xscale = geoStruct.ModelPixelScaleTag[0]
        yscale = geoStruct.ModelPixelScaleTag[1]
        tp = geoStruct.ModelTiePointTag[3:4]
        
        dims = geoInfo.dimensions
        xsize = dims[0]
        ysize = dims[1]
        
        xOrigin = tp[0]
        yOrigin = tp[1] - (yscale * ysize)
        xEnd = xOrigin + (xscale * xsize)
        yEnd = tp[1]
        
        ; Some UTM projections indicate southern hemisphere scenes with negative
        ; values for the lats. These have to have 10e6 added to them to work in IDL.
        IF Keyword_Set(UTM_SOUTH) THEN BEGIN
           yOrigin = yOrigin + 10e6
           yEnd = yEnd + 10e6
        ENDIF
        
        boundary = [xOrigin, yOrigin, xEnd, yEnd]
        xrange = boundary[[0,2]]
        yrange = boundary[[1,3]]
        RETURN, 1
        
    ENDIF 
    
    ; Is this an netCDF file?
    isNCDF = NCDF_IsValidFile(filename)
    
    IF isNCDF THEN BEGIN
        mapCoord = cgNCDFMap(filename, XRANGE=xrange, YRANGE=yrange, $
            SUCCESS=success, /SILENT, USE_LATLON=use_latlon)
        IF success THEN BEGIN
            boundary = [xrange[0], yrange[0], xrange[1], yrange[1]]
            xrange = boundary[[0,2]]
            yrange = boundary[[1,3]]
            RETURN, 1
        ENDIF
    ENDIF
    
    ; Maybe it is a GPD file produced from the GPD_Viewer.
    ; If so, compute the limits from the file.
    root_name = cgRootName(filename, EXTENSION=ext)
    IF StrLowCase(ext) NE 'gpd' THEN $
        Message, 'Do not recognize the file as one for which a ' + $
                 'grid boundary can be computed.'
                     
    ; Read the file.
    lines = File_Lines(filename)
    OpenR, lun, filename, /Get_Lun
    data = StrArr(lines)
    ReadF, lun, data
    Free_Lun, lun
    upData = StrUpCase(data)
        
    ; Can you find the origins of the upper left corner and the grid
    ; height and width?
    loc = StrPos(upData, 'MAP ORIGIN X:')
    index = Where(loc NE -1, count)
    IF count GT 0 THEN BEGIN
        thisLine = data[index[0]]
        x0 = 0.0D
        ReadS, thisLine, x0, FORMAT ='(30x, D0)'
    ENDIF
    loc = StrPos(upData, 'GRID UPPER-LEFT X:')
    index = Where(loc NE -1, count)
    IF count GT 0 THEN BEGIN
        thisLine = data[index[0]]
        x0 = 0.0D
        ReadS, thisLine, x0, FORMAT ='(30x, D0)'
    ENDIF
    loc = StrPos(upData, 'MAP ORIGIN Y:')
    index = Where(loc NE -1, count)
    IF count GT 0 THEN BEGIN
        thisLine = data[index[0]]
        y1 = 0.0D
        ReadS, thisLine, y1, FORMAT ='(30x, D0)'
    ENDIF
    loc = StrPos(upData, 'GRID UPPER-LEFT Y:')
    index = Where(loc NE -1, count)
    IF count GT 0 THEN BEGIN
        thisLine = data[index[0]]
        y1 = 0.0D
        ReadS, thisLine, y1, FORMAT ='(30x, D0)'
    ENDIF
    loc = StrPos(upData, 'GRID MAP UNITS PER CELL:')
    index = Where(loc NE -1, count)
    IF count GT 0 THEN BEGIN
        thisLine = data[index[0]]
        mapunits = 0.0D
        ReadS, thisLine, mapunits, FORMAT ='(30x, F0)'
    ENDIF
    loc = StrPos(upData, 'MAP SCALE:')
    index = Where(loc NE -1, count)
    IF count GT 0 THEN BEGIN
        thisLine = data[index[0]]
        mapscale = 0.0D
        ReadS, thisLine, mapscale, FORMAT ='(30x, F0)'
    ENDIF
    loc = StrPos(upData, 'GRID WIDTH:')
    index = Where(loc NE -1, count)
    IF count GT 0 THEN BEGIN
        thisLine = data[index[0]]
        cols = 0L
        ReadS, thisLine, cols, FORMAT ='(30x, I0)'
    ENDIF
    loc = StrPos(upData, 'GRID HEIGHT:')
    index = Where(loc NE -1, count)
    IF count GT 0 THEN BEGIN
    thisLine = data[index[0]]
    rows = 0L
    ReadS, thisLine, rows, FORMAT ='(30x, I0)'
    ENDIF
    IF (N_Elements(x0) NE 0) AND $
       (N_Elements(y1) NE 0) AND $
       (N_Elements(mapunits) NE 0) AND $
       (N_Elements(mapscale) NE 0) AND $
       (N_Elements(cols) NE 0) AND $
       (N_Elements(rows) NE 0) THEN BEGIN
   
        x1 = x0 + cols*mapunits*mapscale
        y0 = y1 - rows*mapunits*mapscale

        boundary = [x0, y0, x1, y1]
        xrange = boundary[[0,2]]
        yrange = boundary[[1,3]]
        RETURN, 1
    ENDIF 

    ; Can't find boundary, report no success.
    RETURN, 0

END
