##############################################################################
# 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"
}