##############################################################################

# ioos.tcl
#
# DESCRIPTION
#   This file contains the routines used to interface to IOOS-DIF XML
#
# HISTORY
# 10/2008 Arthur Taylor (MDL): Created.
#
# NOTES
# 1) For guidance on the IOOS-DIF XML (NOS perspective) see:
#    http://opendap.co-ops.nos.noaa.gov/ioos-dif-sos/
# 2) I think 'HTTP/GET' is faster than 'HTTP/POST'
#    XML querries are done in the following procedures:
#      proc GetActiveNOS {filename} : Uses 'HTTP/GET' method.
#      proc GetWaterLevel {Text urn}: Uses 'HTTP/GET' method.
##############################################################################

# The tdom package allows us to parse XML.
package require tdom

# The http package allows us to interact with http servers.

package require http 2.0

 

##############################################################################

# PopUp_HTTP_Status() -- Arthur Taylor / MDL

#
# PURPOSE
#   Provides a simple Pop-Up window for informing the user that we are doing
# an HTTP request.
#
# ARGUMENTS
# cmd = Command to perform: open or close window. (Input)
#
# RETURNS: void
#
# HISTORY
# 11/2008 Arthur Taylor (MDL): Created.
#
# NOTES
# 1) Take the focus away from main window.
# 2) May want a USER close/cancel option.
##############################################################################
proc PopUp_HTTP_Status {cmd} {

  catch {destroy .httpStatus}

  if {$cmd == "close"} {

    return

  }

  set tl [toplevel .httpStatus]
  wm title $tl "HTTP Querry Status"
  set cur2 [frame $tl.txt]
    Scrolled_Text $cur2 $cur2.text -relief sunken -bd 2 \
          -wrap none -width 45 -height 25 -setgrid 1 -tabs {20p} \
          -keepScroll
  pack $tl.txt -side top -expand yes -fill both
  return $tl.txt.text
}
 

##############################################################################

# Print() -- Arthur Taylor / MDL

#
# PURPOSE
#   Prints some text into a generic text window.
#
# ARGUMENTS
# TextWindow = text window to add text to. (Input)
#     string = text to add. (Input)
#  f_newline = True if a new line is desired after the text. (Input)
#
# RETURNS: void
#
# HISTORY
# 11/2008 Arthur Taylor (MDL): Created.
#
# NOTES
##############################################################################
proc Print {TextWindow string {f_newline 1}} {

  $TextWindow configure -state normal

  $TextWindow insert end $string
  if {$f_newline} {
    $TextWindow insert end "\n"
  }
  $TextWindow configure -state disabled
  $TextWindow see end
  update
}
 

 

##############################################################################

# Progress() -- Arthur Taylor / MDL

#
# PURPOSE
#   Prints a '.' to update the prgress of http transfers.  Sends this either
# to stdout, or to a TextWindow depending on if the user called
# http::SetProgress.
#
# ARGUMENTS
# TxtWin = For SetProgress: The text window to add '.' to (0 == stdout). (In)
#
# RETURNS: void
#
# HISTORY
# 11/2008 Arthur Taylor (MDL): Created.
#
# NOTES
##############################################################################
namespace eval http {

  set TextWindow 0

  proc SetProgress {TxtWin} {

    variable TextWindow

    set TextWindow $TxtWin

  }

  proc Progress {args} {

    variable TextWindow
    if {$TextWindow == 0} {
      puts -nonewline stderr . ; flush stderr
    } else {
      Print $TextWindow "HERE ." 0
    }
    update
  }
}

 

##############################################################################

# GetActiveNOS() -- Arthur Taylor / MDL

#
# PURPOSE
#   Request the current active stations from NOS.  Parse the response and
# store in the provided filename.
#
# ARGUMENTS
# filename = Store the results in this file. (Output)
#
# RETURNS: int
# 1 on error (couldn't load the stations)
#
# HISTORY
# 10/2008 Arthur Taylor (MDL): Created.
#
# NOTES
# 1) Take the focus away from main window.
# 2) May want a USER close/cancel option.
# 3) Uses 'HTTP/GET' method.  I think the 'HTTP/GET' is faster than
#    the 'HTTP/POST' method.
##############################################################################
proc GetActiveNOS {filename} {
  # Set up our active station request

#  set SoapRequest {<?xml version="1.0" encoding="UTF-8"?>

#    <sos:GetCapabilities

#      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

#      xsi:schemaLocation="http://schemas.opengis.net/sos/1.0.0/sosAll.xsd"

#      xmlns:sos="http://www.opengis.net/sos/1.0"

#      xmlns:gml="http://www.opengis.net/gml/3.2"

#      xmlns:ogc="http://www.opengis.net/ogc"

#      xmlns:om="http://www.opengis.net/om/1.0" service="SOS">

#    </sos:GetCapabilities>}

#  set URL "http://opendap.co-ops.nos.noaa.gov/ioos-dif-sos/SOS"

  set querry [::http::formatQuery service SOS \

              request GetCapabilities]

  set URL "http://opendap.co-ops.nos.noaa.gov/ioos-dif-sos/SOS?$querry"

 

  # Get the response to our active station request

  set txt [PopUp_HTTP_Status open]

  Print $txt "sending request for active stations."

#  Print $txt "to: http://opendap.co-ops.nos.noaa.gov/ioos-dif-sos/SOS"

  Print $txt "to: $URL"

  ::http::SetProgress $txt

#  if {[catch {::http::geturl $URL -progress ::http::Progress \

#              -query $SoapRequest -headers [list SOAPAction ""]} token]} {

#    Print $txt "Error Geturl : Message $token"
#    return 1
#  }
  if {[catch {::http::geturl $URL -progress ::http::Progress \
              -blocksize 8192} token]} {

    Print $txt "Error Geturl : Message $token"
    return 1
  }

  Print $txt ""
  Print $txt "Finished receiving active stations"

  PopUp_HTTP_Status close

  ::http::SetProgress 0

 

  # Parse the resulting xml (in $state(body)), and store in $filename

  upvar #0 $token state

  set fp [open $filename w]

  puts $fp "# NOS Active station ID, URN, Lat, Lon"

  set doc [dom parse $state(body)]

  set root [$doc documentElement]

  set nodes [$root getElementsByTagName ObservationOffering]

  foreach n1 $nodes {

    foreach obsType [$n1 getElementsByTagName observedProperty] {

      set type [$obsType getAttribute xlink:href]

      if {$type == "urn:ogc:def:property:OGC:waterlevel"} {

        set pos [[$n1 getElementsByTagName gml:lowerCorner] text]

        set urn [[$n1 getElementsByTagName gml:name] text]

        set id [lindex [split [$n1 getAttribute gml:id] -] 1]

        puts $fp "$id, $urn, [lindex $pos 0], [lindex $pos 1]"

        break

      }

    }

  }

  close $fp

  return 0

}

 

##############################################################################

# slosh_NOS_StationLoad() -- Arthur Taylor / MDL

#
# PURPOSE
#   Load the NOS stations from file.  If the file doesn't exist then call
# GetActiveNOS to create it and attempt to continue.
#
# ARGUMENTS
# ray_name = Global array of variables. (Input/Output)
# filename = Read the NOS Statiosn from this file (create if needed) (In/Out)
#
# RETURNS: int
# 1 on error (couldn't load the stations)
#
# HISTORY
# 10/2008 Arthur Taylor (MDL): Created.
#
# NOTES
##############################################################################
proc slosh_NOS_StationLoad {ray_name filename} {

  upvar #0 $ray_name ray

 

  # Attempt to make sure $filename exists.

  if {! [file exists $filename]} {

    if {[GetActiveNOS $filename]} {

      tk_messageBox -message "Unable to download the Active stations."

      return 1

    }

  }

  if {! [file exists $filename]} {

    tk_messageBox -message "Unable to get the Active stations from \

                            $filename, or online."

    return 1

  }

 

  set fp [open $filename r]

  set ray(NOS_StationList) ""

  while {[gets $fp line] > 0} {

    if {[string index $line 0] != "#"} {

      lappend ray(NOS_StationList) [split $line ,]

    }

  }

  close $fp

  return 0

}

 

##############################################################################

# slosh_NOS_StationDraw() -- Arthur Taylor / MDL

#
# PURPOSE
#   Draw the NOS stations onto the map.  If the stations haven't been loaded,
# it attempts to do so.
#
# ARGUMENTS
# ray_name = Global array of variables. (Input/Output)
#
# RETURNS: int
# 1 on error (couldn't load the stations)
#
# HISTORY
# 10/2008 Arthur Taylor (MDL): Created.
#
# NOTES
##############################################################################
proc slosh_NOS_StationsDraw {ray_name} {

  upvar #0 $ray_name ray

 

  # Attempt to load the Stations List

  if {! [info exists ray(NOS_StationList)]} {

    if {[slosh_NOS_StationLoad $ray_name \

         [file join $ray(SRC_dir) nosstat.txt]]} {

      return 1

    }

  }

  catch {$ray(canv) delete withtag NOS_Stations}

  # Get the dimmensions of the window.

  set temp [halo_ZoomInquire $ray(Zwin)]

  set wid [lindex $temp 4]

  set hei [lindex $temp 5]

  foreach elem $ray(NOS_StationList) {
    # Next 2 commands convert from regular lat/lon to screen coordinates

    set t3 [halo_ConvertMerc2 $ray(Zwin) 0 [lindex $elem 2] \

            [expr -1 * [lindex $elem 3]]]

    set point [halo_ZoomConvert $ray(Zwin) 0 [lindex $t3 0] [lindex $t3 1]]
    set px [lindex $point 0]
    set py [lindex $point 1]

    if {($px >= 0) && ($py >= 0) && ($px < $wid) && ($py < $hei)} {
      set x1 [expr $px - 2]

      set y1 [expr $py - 2]

      set x2 [expr $px + 2]

      set y2 [expr $py + 2]

      $ray(canv) create rectangle $x1 $y1 $x2 $y2 -fill "orange" \

            -tags "Main NOS_Stations"

    }

  }

  catch {$ray(canv) raise noaa NOS_Stations}

  catch {$ray(canv) raise dist_ver NOS_Stations}
  catch {$ray(canv) raise dist_hor NOS_Stations}
  return 0
}

 

##############################################################################

# GetWaterLevel() -- Arthur Taylor / MDL

#
# PURPOSE
#   Request a time history (from 48 hours before now to now) of the water
# level from NOS.
#
# ARGUMENTS
# txt = text window to add text to. (Input)
# urn = 'Uniform Resource Name' for the observation point in question (Input)
#
# RETURNS: int
# 1 on http protocol error
# 2 there was no data
#
# HISTORY
# 10/2008 Arthur Taylor (MDL): Created.
#
# NOTES
# 1) Uses 'HTTP/GET' method.  Don't know if this is faster / slower to
#    'HTTP/POST' method.
##############################################################################
proc GetWaterLevel {txt urn} {

  # Set up our querry:

  set now [clock seconds]

  set nowM48 [expr $now - 48 * 3600]

  set t1 [clock format $nowM48 -gmt 1 -format "%Y-%m-%dT%H:%M:%SZ"]

  set t2 [clock format $now -gmt 1 -format "%Y-%m-%dT%H:%M:%SZ"]

  set querry [::http::formatQuery service SOS \

              request GetObservation \

              version 1.0.0 \

              observedProperty waterlevel \

              offering $urn \

              responseFormat text/xml\;schema=\"ioos/0.6.1\" \

              eventTime $t1/$t2]

  set URL "http://opendap.co-ops.nos.noaa.gov/ioos-dif-sos/SOS?$querry"

 

  # Get the response to our querry

  Print $txt "Sending request for water level"

  Print $txt "to: $URL"

  set StartTime [clock milliseconds]

  ::http::SetProgress $txt

  if {[catch {::http::geturl $URL -progress ::http::Progress \

              -blocksize 8192} token]} {

    Print $txt "Error Geturl : Message $token"
    return 1
  }

  Print $txt ""
  Print $txt "Finished receiving response: Took \
              [expr [clock milliseconds] - $StartTime] milliseconds"
  ::http::SetProgress 0

 

  # Parse the resulting xml (in $state(body))

  upvar #0 $token state

  set doc [dom parse $state(body)]

  set root [$doc documentElement]

 

  # Check if there is data.

  set pntObs [$root selectNodes om:result/ioos:Composite/gml:valueComponents]

  if {$pntObs == ""} {

    Print $txt "No Data"

    Print $txt "$state(body)"

    return 2

  }

 

  # Parse the list of observations.

  set path [list ioos:Array gml:valueComponents ioos:Composite \

            gml:valueComponents]

  set timeSeries [$pntObs selectNodes [join $path /]]

  set numElem [[$timeSeries selectNodes ioos:Count] text]

  Print $txt $numElem

  set path [list ioos:Array gml:valueComponents ioos:Composite]

  set timeList [$timeSeries selectNodes [join $path /]]

  if {[llength $timeList] != $numElem} {

    tk_messageBox -message "Error... Count != number of time elements"

  }

  foreach n1 $timeList {

    set time [[$n1 getElementsByTagName gml:timePosition] text]

    set value [[$n1 getElementsByTagName ioos:Quantity] text]

    Print $txt "$time $value"

  }

 

  # Print the station name

  set path [list om:procedure om:Process ioos:CompositeContext \

            gml:valueComponents ioos:ContextArray gml:valueComponents \

            ioos:CompositeContext gml:valueComponents ioos:StationName]

  set node [$root selectNodes [join $path /]]

  Print $txt [$node text]

  return 0

}

 

##############################################################################

# slosh_NOS_Station_Probe() -- Arthur Taylor / MDL

#
# PURPOSE
#   Shifts into mode where a click will result in a time history of the
# observations from the nearest NOS station for the last 48 hours.
#
# ARGUMENTS
# ray_name = Global array of variables. (Input/Output)
#      x y = Screen coordinates the user has clicked on (Input)
#     flag = 0 (start), 1 (button press in map), 3 (cancel).
#
# RETURNS: void
#
# HISTORY
# 10/2008 Arthur Taylor (MDL): Created.
#
# NOTES
# 1) May want to save state of <1> before Start, and reset it in Cancel.
##############################################################################
proc slosh_NOS_Station_Probe {ray_name x y flag} {

  upvar #0 $ray_name ray

  set c $ray(canv)

 

  # Start (Shift us into slosh_NOS_Station_Probe mode.

  if {$flag == 0} {

    AT_ZoomNone $ray_name $x $y 1
    set ray(mode_cmd) slosh_NOS_Station_Probe
    # set state [bind $c <1>]
    bind $c <1> "AT_MidEmu $ray_name 1 \"slosh_NOS_Station_Probe \
                 $ray_name %x %y 1\""
    $c configure -cursor fleur
    set ray(cursor) fleur
    # Draw the NOS stations if they haven't been drawn yet.
    if {$ray(f_NOS_Stations) != 1} {
      set ray(f_NOS_Stations) 1
      AT_PauseUser $ray(canv) 1
      slosh_RedrawMain $ray_name 1 0
      set G_cursor [join "$ray_name (cursor)" ""]
      AT_PauseUser $ray(canv) $G_cursor
    }
    return
  }
 

  # Cancel (Shift us out of slosh_NOS_Station_Probe mode).

  if {$flag == 3} {
    # bind $c <1> $state
    $c configure -cursor arrow
    set ray(cursor) arrow
    return
  }
 

  # Handle a button press in the map.
  if {$flag == 1} {
    # Find nearest station.  It is possible there is none.
    set tmp [halo_ZoomConvert $ray(Zwin) 1 [$ray(canv) canvasx $x] \
             [$ray(canv) canvasy $y]]
    set temp [halo_ConvertMerc2 $ray(Zwin) 1 [lindex $tmp 0] [lindex $tmp 1]]
    set lat1 [lindex $temp 0]
    set lon1 [expr -1 * [lindex $temp 1]]
    set minElem ""
    foreach elem $ray(NOS_StationList) {
      set lat2 [lindex $elem 2]
      set lon2 [lindex $elem 3]
      set d2 [expr ($lat1 - $lat2) * ($lat1 - $lat2) + \
                   ($lon1 - $lon2) * ($lon1 - $lon2)]
      if {($minElem == "") || ($d2 < $minD)} {
        set minElem $elem
        set minD $d2
      }
    }

    # Pop Up window with IOOS data for that station.
    catch {destroy .nosLevel}
    set tl [toplevel .nosLevel]
    wm title $tl "[lindex $minElem 0] [lindex $minElem 1]"
    label $tl.lab -text "[lindex $minElem 0] [lindex $minElem 1]"
    pack $tl.lab -side top -expand no -fill x

    # Build the graph Frame


    # Build the txt Frame
    set cur2 [frame $tl.txt]
      Scrolled_Text $cur2 $cur2.text -relief sunken -bd 2 \
            -wrap none -width 45 -height 25 -setgrid 1 -tabs {20p} \
            -keepScroll
    pack $tl.txt -side top -expand yes -fill both
    GetWaterLevel $tl.txt.text [lindex $minElem 1]
    return
  }

  tk_messageBox -message "Invalid flag $flag to slosh_NOS_Station_Probe"
}