1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
import DWML
import datetime
import pyxb.utils.domutils as domutils
import pyxb.binding.datatypes as xsd
import pyxb.xmlschema.structures as structures
import urllib2
import time
import sys
import pyxb.bundles.wssplat.soap11 as soapenv
today = xsd.dateTime.today()
later = today + datetime.timedelta(days=7)
# Set a standard position for which we want the weather
lat = 38.898748
lon = -77.037684
# Override the position from the command line
if 2 < len(sys.argv):
lat = float(sys.argv[1])
lon = float(sys.argv[2])
# Import the schema bindings that were extracted from the WSDL in a
# separate, previous step.
import ndfd
# Read in the WSDL spec for the service. Note that we have to process
# the schema again here, because we were unable to save the component
# model for it before, and we need the definition maps in order to
# resolve part type references in the WSDL messages.
import pyxb.bundles.wssplat.wsdl11 as wsdl
uri_src = open('ndfdXML.wsdl')
doc = domutils.StringToDOM(uri_src.read())
spec = wsdl.definitions.createFromDOM(doc.documentElement, process_schema=True)
# Create a helper that will generate XML in the WSDL's namespace,
# qualifying every element with xsi:type just like the service
# expects.
bds = domutils.BindingDOMSupport(default_namespace=spec.targetNamespace(), require_xsi_type=True)
# Set the parameters that you want enabled. See
# http://www.nws.noaa.gov/xml/docs/elementInputNames.php
weather_params = ndfd.weatherParametersType(maxt=True, mint=True, temp=True, sky=True, pop12=True, rh=True, wx=True, appt=True)
# The schema didn't say the other parameters are optional (even though
# they are), so set them to false if not already initialized.
for eu in weather_params._ElementMap.values():
if eu.value(weather_params) is None:
eu.set(weather_params, False)
# There is no schema element or type corresponding to the request
# message; it's only in a WSDL message definition. We need to build
# it manually.
# Create a root element corresponding to the operation's input message
root = bds.createChildElement('NDFDgen')
# Create a map from the message part name to the value to use for that
# part.
request_values = { 'latitude' : lat
, 'longitude' : lon
, 'startTime' : today
, 'endTime' : later
, 'product' : ndfd.productType.time_series
, 'weatherParameters' : weather_params }
# Get the WSDL message description, and for each part for which we
# have a value, add an element to the message document.
req_msg = spec.messageMap()['NDFDgenRequest']
for p in req_msg.part:
fv = request_values.get(p.name, None)
if fv is None:
print '%s: %s has no value' % (p.name, p.typeReference.expandedName())
else:
print '%s: %s' % (p.name, p.typeReference.expandedName())
# Make sure the value is of the expected type
type_binding = p.typeReference.expandedName().typeBinding()
if not isinstance(fv, type_binding):
fv = type_binding.Factory(fv)
fv.toDOM(bds, root, element_name=p.name)
# Finish the request message and get it as a DOM document.
dom = bds.finalize()
# We don't have a facility to add DOM values (as opposed to binding
# instances) to a soap binding instance, so just directly generate the
# message by wedging the request into a generic SOAP envelope body.
soap_message = '''<?xml version="1.0" encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>''' + dom.documentElement.toxml() + '''</SOAP-ENV:Body>
</SOAP-ENV:Envelope>'''
#soap_message = file('NDFDgen.xml').read()
#soap_message = file('test.xml').read()
# Save the request message so it can be examined later
file('req.xml', 'w').write(soap_message)
# Execute the request
endpoint = 'http://www.weather.gov/forecasts/xml/SOAP_server/ndfdXMLserver.php'
soap_action = 'http://www.weather.gov/forecasts/xml/DWMLgen/wsdl/ndfdXML.wsdl#NDFDgen'
uri = urllib2.Request(endpoint,
soap_message,
{ 'SOAPAction' : soap_action, 'Content-Type': 'text/xml' } )
rxml = urllib2.urlopen(uri).read()
#rxml = file('resp.xml').read()
# Save the raw SOAP-wrapped response
file('rawresp.xml', 'w').write(rxml)
# The NDFD interface is "interesting" in that the response message for
# the SOAP interface is encoded as a text string, rather than being
# provided as XML directly. The noise below extracts it.
rdom = domutils.StringToDOM(rxml)
resp = soapenv.CreateFromDOM(rdom)
v = resp.Body.wildcardElements()[0]
rxml = v.childNodes[0].childNodes[0].value
# Save the extracted response
file('resp.xml', 'w').write(rxml)
#rxml = file('resp.xml').read()
# Create the binding instance from the response, and start spitting
# out its data.
r = DWML.CreateFromDOM(domutils.StringToDOM(rxml))
product = r.head.product
print '%s %s' % (product.title, product.category)
source = r.head.source
print ", ".join(source.production_center.content())
data = r.data
for i in range(len(data.location)):
loc = data.location[i]
print '%s [%s %s]' % (loc.location_key, loc.point.latitude, loc.point.longitude)
for p in data.parameters:
if p.applicable_location != loc.location_key:
continue
mint = maxt = None
for t in p.temperature:
if 'maximum' == t.type:
maxt = t
elif 'minimum' == t.type:
mint = t
print '%s (%s): %s' % (t.name, t.units, " ".join([ str(_v.value()) for _v in t.value_ ]))
time_layout = None
for tl in data.time_layout:
if tl.layout_key == mint.time_layout:
time_layout = tl
break
for ti in range(len(time_layout.start_valid_time)):
start = time_layout.start_valid_time[ti].value()
end = time_layout.end_valid_time[ti]
print '%s: min %s, max %s' % (time.strftime('%A, %B %d %Y', start.timetuple()),
mint.value_[ti].value(), maxt.value_[ti].value())
|