; docformat = 'rst'
;
; NAME:
;   cgMapPlotS
;
; PURPOSE:
;   This object is a wrapper for the cgPlotS routine in IDL. It provides a simple 
;   way to allow lines and symbols to be placed as annotations on map projections
;   created with the cgMap coordinate object.
;
;******************************************************************************************;
;                                                                                          ;
;  Copyright (c) 2011, 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.                            ;
;******************************************************************************************;
;
;+--------------------------------------------------------------------------
;   This object is a wrapper for the cgPlotS routine in IDL. It provides a simple 
;   way to allow lines and symbols to be placed as annotations on map projections
;   created with the cgMap coordinate object. Note that it may be easier and more
;   convenient to use cgPlotS, if you don't need a complete object solution.
;
; :Categories:
;    Graphics, Map Projections
;    
; :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:
;     Change History::
;        Written by David W. Fanning, 21 November 2011.
;        Never tested, for some reason. Now, several bugs have been fixed and the 
;           program works as advertised. 17 Sept 2012. DWF.
;                
; :Copyright:
;     Copyright (c) 2011, Fanning Software Consulting, Inc.
;---------------------------------------------------------------------------
;
;+--------------------------------------------------------------------------
;   The initialization method for the object.
;
; :Params:
;    mapCoord: in, required, type=object
;       A map coordinate object that will set up a map coordinate data space.
;       Required to convert lat/lon values to projected meter space. A cgMap object.
;       
; :Keywords:
;     addcmd: in, optional, type=boolean, default=0
;        If this keyword is set, the object is added to the resizeable graphics
;        window, cgWindow. The DRAW method of the object is called in cgWindow.
;     clip: in, optional, type=fltarr(4)
;        The coordinates of a rectangle used to clip the graphics output. 
;        The rectangle is specified as a vector of the form [X0, Y0, X1, Y1], 
;        giving coordinates of the lower left and upper right corners, 
;        respectively. The default clipping rectangle is the plot window set
;        up by the cgMap object. 
;     color: in, optional, type=string, default="opposite"
;        The name of the color to draw the grid lines in. 
;     lats: in, optional, type=float
;        The latitude values to draw.
;     linestyle: out, optional, type=integer, default=1 
;        Set this keyword to the type of linestyle desired. See Graphics Keywords in
;        the on-line help for additional information.
;     lons: in, optional, type=float
;        The longitude values to draw.
;     noclip: in, optional, type=boolean, default=0
;        Set this keyword to supress clipping of the plot.
;     psym: in, optional, type=integer, default=0
;        The plotting symbol to use for the plot. Can use any symbol available in
;        the Coyote Library routine SYMCAT.
;     symsize: in, optional, type=float, default=1.0
;        Set this keyword to the size of symbols.
;     t3d: in, optional, type=boolean, default=0
;        Set this graphics keyword if you wish to draw using the T3D transformation matrix.
;     thick: in, optional, type=integer, default=1
;        Set this keyword to the thickness of the line used to draw the grid.
;     uvcoords: in, optional, type=boolean, default=0
;        Set this keyword if the LONS and LATS are specified in UV (XY) coordinates, rather than
;        longitude and latitude coordinates.
;     zvalue: in, optional, type=float, default=0.0
;        Set this keyword to the ZVALUE where the output should be drawn.
;     _extra: in, optional
;        Any keywords appropriate for superclass objects are also permitted.
;---------------------------------------------------------------------------
FUNCTION cgMapPlotS::INIT, mapCoord, $
    ADDCMD=addcmd, $
    CLIP=clip, $
    COLOR=color, $
    LATS=lats, $
    LINESTYLE=linestyle, $
    LONS=lons, $
    NOCLIP=noclip, $
    PSYM=psym, $
    SYMSIZE=symsize, $
    T3D=t3d, $
    THICK=thick, $
    UVCOORDS=uvcoords, $
    ZVALUE=zvalue, $
    _EXTRA=extra
    
    ; Error handling
    Catch, theError
    IF theError NE 0 THEN BEGIN
        Catch, /Cancel
        void = cgErrorMsg()
        RETURN, 0
    ENDIF
    
    ; Make sure you have a valid map object or there is no point in going ahead.
    IF ~Obj_Valid(mapCoord) THEN Message, 'A valid map object is required to create a cgMapPlotS object.'
    
    ; Initialize superclass object,
     ok = self -> cgContainer::INIT(_EXTRA=extra) 
     IF ~ok THEN RETURN, 0

    ; Default values.
    SetDefaultValue, color, 'opposite'
    SetDefaultValue, linestyle, 0
    SetDefaultValue, noclip, 1
    SetDefaultValue, psym, 0
    SetDefaultValue, symsize, 1.0
    SetDefaultValue, t3d, 0
    SetDefaultValue, thick, 1.0
    SetDefaultValue, uvcoords, 0B
    SetDefaultValue, zvalue, 0.0
    IF N_Elements(clip) NE 0 THEN self.clip = clip
    self.color = color
    self.linestyle = linestyle
    self.noclip = noclip
    self.psym = psym
    self.symsize = symsize
    self.t3d = t3d
    self.thick = thick
    self.uvcoords = uvcoords
    self.zvalue = zvalue
    
    ; If no longitudes yet, create a valid pointer to them.
    IF N_Elements(lons) EQ 0 $
        THEN self.lons = Ptr_New(/ALLOCATE_HEAP) $
        ELSE self.lons = Ptr_New(lons)
    
    ; If no latitudes yet, create a valid pointer to them.
    IF N_Elements(lats) EQ 0 $
        THEN self.lats = Ptr_New(/ALLOCATE_HEAP) $
        ELSE self.lats = Ptr_New(lats)
        
    ; Store the map object. 
    self.mapCoord = mapCoord
        
    ; Need to add this command to a resizeable cgWindow?
    IF Keyword_Set(addcmd) THEN self -> AddCmd
    
    RETURN, 1
    
END 


;+--------------------------------------------------------------------------
;   Adds the object as a command (the DRAW method is called) in a cgWindow 
;   resizeable graphics window. 
;
;---------------------------------------------------------------------------
PRO cgMapPlotS::AddCmd

   cgWindow, "Draw", self, /Method, /AddCmd 
   
END 


;+--------------------------------------------------------------------------
;   Draws the line or symbol on the map projection.
;---------------------------------------------------------------------------
PRO cgMapPlotS::Draw

    ; Error handling
    Catch, theError
    IF theError NE 0 THEN BEGIN
        Catch, /Cancel
        void = cgErrorMsg()
        RETURN
    ENDIF
    
    ; You have to have data to plot. If not exit quietly.
    IF (N_Elements(*self.lons) EQ 0) OR (N_Elements(*self.lats) EQ 0) THEN RETURN
    
    ; Find a map structure, if you can.
    IF Obj_Valid(self.mapCoord) THEN BEGIN
        mapStruct = self.mapCoord -> GetMapStruct() 
        self.mapCoord -> Draw, /NoGraphics
    ENDIF ELSE Message, 'There is no valid map object from which a map structure can be obtained.'

    ; If you have a map structure, then determine if the points to plot
    ; are in lat/lon or UV coordinate space. The cgMap object sets up
    ; a UV coordinate space. The values to be plotted here are in lat/lon
    ; space, so they have to be converted to XY space to be plotted.
    IF N_Elements(mapStruct) NE 0 THEN BEGIN
    
        ; If the "lons and lats" are already in UVCOORDS, leave them alone.
        IF self.uvcoords THEN BEGIN
            lons = *self.lons
            lats = *self.lats
        ENDIF ELSE BEGIN
        
            ; Otherwise, convert them, since the map is *always* in UVCoords.
            uv = MAP_PROJ_FORWARD(*self.lons, *self.lats, MAP_STRUCTURE=mapStruct)
            lons = Reform(uv[0,*])
            lats = Reform(uv[1,*])
        ENDELSE
     ENDIF ELSE BEGIN
        lons = *self.lons
        lats = *self.lats
    ENDELSE
    
;    ; Accommodate SYMCAT symbols
;    IF self.psym GE 0 THEN psym = SymCat(self.psym) ELSE psym = (-1) * SymCat(Abs(self.psym))
;    
    ; If clip is not defined, then set it here.
    IF Total(self.clip) EQ 0 $
        THEN clip = [!X.CRange[0], !Y.CRange[0], !X.CRange[1], !Y.CRange[1]] $
        ELSE clip = self.clip

    ; Draw the lines or symbols.
    cgPlotS, lons, lats,  $
        CLIP=clip, $
        COLOR=self.color, $
        LINESTYLE=self.linestyle, $
        NOCLIP=self.noclip, $
        PSYM=self.psym, $
        SYMSIZE=self.symsize, $
        T3D=self.t3d, $
        THICK=self.thick, $
        Z=self.zvalue

END 

    
;+--------------------------------------------------------------------------
;   This method allows the user to get various properties of the object. 
;   The same keywords that are used for the INIT method can be used here.
;       
; :Keywords:
;     clip: out, optional, type=fltarr(4)
;        The coordinates of a rectangle used to clip the graphics output. 
;        The rectangle is specified as a vector of the form [X0, Y0, X1, Y1], 
;        giving coordinates of the lower left and upper right corners, 
;        respectively. The default clipping rectangle is the plot window set
;        up by the cgMap object. 
;     color: out, optional, type=string, default="opposite"
;        The name of the color to draw the grid lines in. 
;     lats: out, optional, type=float
;        The latitude values to draw.
;     linestyle: out, optional, type=integer, default=1 
;        Set this keyword to the type of linestyle desired. See Graphics Keywords in
;        the on-line help for additional information.
;     lons: out, optional, type=float
;        The longitude values to draw.
;     mapcoord: out, optional, type=object
;        Get the map coordinate object.
;     noclip: out, optional, type=boolean, default=0
;        Set this keyword to supress clipping of the plot.
;     psym: out, optional, type=integer, default=0
;        The plotting symbol to use for the plot. Can use any symbol available in
;        the Coyote Library routine SYMCAT.
;     symsize: out, optional, type=float, default=1.0
;        Set this keyword to the size of symbols.
;     t3d: out, optional, type=boolean, default=0
;        Set this graphics keyword if you wish to draw using the T3D transformation matrix.
;     thick: out, optional, type=integer, default=1
;        Set this keyword to the thickness of the line used to draw the grid.
;     uvcoords: out, optional, type=boolean, default=0
;        Set this keyword if the LONS and LATS are specified in UV (XY) coordinates, rather than
;        longitude and latitude coordinates.
;     zvalue: out, optional, type=float, default=0.0
;        Set this keyword to the ZVALUE where the output should be drawn.
;     _ref_extra: in, optional
;        Any keywords appropriate for superclass objects are also permitted.
;
;---------------------------------------------------------------------------
PRO cgMapPlotS::GetProperty, $
    CLIP=clip, $
    COLOR=color, $
    LATS=lats, $
    LINESTYLE=linestyle, $
    LONS=lons, $
    NOCLIP=noclip, $
    MAPCOORD=mapcoord, $
    PSYM=psym, $
    SYMSIZE=symsize, $
    T3D=t3d, $
    THICK=thick, $
    UVCOORDS=uvcoords, $
    ZVALUE=zvalue, $
    _REF_EXTRA=extra

    ; Error handling
    Catch, theError
    IF theError NE 0 THEN BEGIN
        Catch, /Cancel
        void = cgErrorMsg()
        RETURN
    ENDIF
    
    clip = self.clip
    color = self.color
    linestyle = self.linestyle
    noclip = self.noclip
    psym = self.psym
    symsize = self.symsize
    t3d = self.t3d
    thick = self.thick
    uvcoords = self.uvcoords
    zvalue = self.zvalue
    lats = *self.lats
    lons = *self.lons
    
    IF N_Elements(extra) NE 0 THEN self -> cgContainer::GetProperty, _EXTRA=extra
    
END 

    
;+--------------------------------------------------------------------------
;   This method allows the user to set various properties of the object. 
;   The same keywords that are used for the INIT method can be used here.
;       
; :Keywords:
;     addcmd: in, optional, type=boolean, default=0
;        If this keyword is set, the object is added to the resizeable graphics
;        window, cgWindow. The DRAW method of the object is called in cgWindow.
;     clip: in, optional, type=fltarr(4)
;        The coordinates of a rectangle used to clip the graphics output. 
;        The rectangle is specified as a vector of the form [X0, Y0, X1, Y1], 
;        giving coordinates of the lower left and upper right corners, 
;        respectively. The default clipping rectangle is the plot window set
;        up by the cgMap object. 
;     color: in, optional, type=string, default="opposite"
;        The name of the color to draw the grid lines in. 
;     lats: in, optional, type=float
;        The latitude values to draw.
;     linestyle: out, optional, type=integer, default=1 
;        Set this keyword to the type of linestyle desired. See Graphics Keywords in
;        the on-line help for additional information.
;     lons: in, optional, type=float
;        The longitude values to draw.
;     noclip: in, optional, type=boolean, default=0
;        Set this keyword to supress clipping of the plot.
;     psym: in, optional, type=integer, default=0
;        The plotting symbol to use for the plot. Can use any symbol available in
;        the Coyote Library routine SYMCAT.
;     symsize: in, optional, type=float, default=1.0
;        Set this keyword to the size of symbols.
;     t3d: in, optional, type=boolean, default=0
;        Set this graphics keyword if you wish to draw using the T3D transformation matrix.
;     thick: in, optional, type=integer, default=1
;        Set this keyword to the thickness of the line used to draw the grid.
;     uvcoords: in, optional, type=boolean, default=0
;        Set this keyword if the LONS and LATS are specified in UV (XY) coordinates, rather than
;        longitude and latitude coordinates.
;     zvalue: in, optional, type=float, default=0.0
;        Set this keyword to the ZVALUE where the output should be drawn.
;     _extra: in, optional
;        Any keywords appropriate for superclass objects are also permitted.
;
;---------------------------------------------------------------------------
PRO cgMapPlotS::SetProperty, $
    ADDCMD=addcmd, $
    CLIP=clip, $
    COLOR=color, $
    LATS=lats, $
    LINESTYLE=linestyle, $
    LONS=lons, $
    NOCLIP=noclip, $
    PSYM=psym, $
    SYMSIZE=symsize, $
    T3D=t3d, $
    THICK=thick, $
    UVCOORDS=uvcoords, $
    ZVALUE=zvalue, $
    _EXTRA=extra

    ; Error handling
    Catch, theError
    IF theError NE 0 THEN BEGIN
        Catch, /Cancel
        void = cgErrorMsg()
        RETURN
    ENDIF
    IF N_Elements(lons) NE 0 THEN BEGIN
        *self.lons = lons
    ENDIF
    IF N_Elements(lats) NE 0 THEN BEGIN
        *self.lats = lats
    ENDIF

    IF N_Elements(addcmd) NE 0 THEN self -> AddCmd
    IF N_Elements(clip) NE 0 THEN self.clip = clip
    IF N_Elements(color) NE 0 THEN self.color = color
    IF N_Elements(linestyle) NE 0 THEN self.linestyle = linestyle
    IF N_Elements(noclip) NE 0 THEN self.noclip = noclip
    IF N_Elements(psym) NE 0 THEN self.psym = psym
    IF N_Elements(symsize) NE 0 THEN self.symsize = symsize
    IF N_Elements(t3d) NE 0 THEN self.t3d = t3d
    IF N_Elements(thick) NE 0 THEN self.thick = thick
    IF N_Elements(zvalue) NE 0 THEN self.zvalue = zvalue

    IF N_Elements(extra) NE 0 THEN self -> cgContainer::SetProperty, _EXTRA=extra

END 


;+--------------------------------------------------------------------------
;   This is the clean-up routine for the object.
;---------------------------------------------------------------------------
PRO cgMapPlotS::CLEANUP

    Ptr_Free, self.lons
    Ptr_Free, self.lats
    
    self -> cgContainer::CLEANUP
END 


;+--------------------------------------------------------------------------
;   This is the class definition module. 
;
; :Params:
;    class: out, optional, type=structure
;       Occasionally, it is useful to have an object class definition as
;       a structure variable. Using this output keyword will allow that.
;---------------------------------------------------------------------------
PRO cgMapPlotS__DEFINE, class

    class = { cgMapPlotS, $
              lons: Ptr_New(), $         
              lats: Ptr_New(), $        
              clip: DblArr(4),$   
              color: "", $               
              linestyle: 0, $
              noclip: 0B, $
              symsize: 0.0, $
              thick: 0, $
              psym: 0, $
              t3d: 0B, $
              zvalue: 0.0, $
              uvcoords: 0B, $
              mapCoord: Obj_New(), $
              INHERITS cgContainer $
            }

END ; -------------------------------------------------------------------------------------