voruby2-preview 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,308 @@
1
+ require 'open-uri'
2
+ require 'mathn'
3
+ require 'date'
4
+ require 'tempfile'
5
+
6
+ begin
7
+ require 'rubygems'
8
+ rescue LoadError; end
9
+ require 'xml/libxml'
10
+ require 'builder'
11
+
12
+ require 'voruby'
13
+ require 'voruby/misc/libxml_ext'
14
+
15
+ module VORuby
16
+ # A set of classes designed to read and manipulate the IVOA[http://www.ivoa.net/]
17
+ # standard for representing astronomical tabular data called
18
+ # VOTable[http://www.ivoa.net/Documents/latest/VOT.html].
19
+ #
20
+ # As a quick start, you might do something like this:
21
+ #
22
+ # require 'voruby/votable/votable'
23
+ # include VORuby
24
+ #
25
+ # votable = VOTable.from_xml(File.new('votable.basic.xml')) # this happens to be a 1.1 votable
26
+ # table = votable.resources.first.tables.first # get the first resource and table
27
+ #
28
+ # # See the name of each column
29
+ # puts table.fields.collect{ |field| field.name }.join('|')
30
+ #
31
+ # # Iterate through each row.
32
+ # table.data.format.trs.each do |row|
33
+ # row_as_string = row.tds.collect{ |td| td.value }.join('|')
34
+ # puts row_as_string
35
+ # end
36
+ #
37
+ # Methods that are pluralized (i.e. table.fields and format.trs above) normally return an
38
+ # array-like object called a HomogeneousNodeList. You can do most of the common array
39
+ # operations on it, including array assignment ([]=) and retrieval ([]), appending (<<)
40
+ # and iteration (each). So:
41
+ #
42
+ # table.fields << Field.new(...) # append a new field
43
+ # table.fields.prepend(Field.new(...)) # prepend a new field
44
+ # table.fields.each { |field| ... } # iterate through each field
45
+ # table.fields[2] = Field.new(...) # assign a new field to the third position
46
+ # table.fields[2] # retrieve the third field
47
+ # table.fields.delete_at(2) # delete the third field
48
+ # table.fields.clear # delete all fields
49
+ # table.fields.first # retrieve the first field
50
+ # table.fields.last # retrieve the last field
51
+ # table.fields.size # the number of fields present
52
+ #
53
+ # It also mixes in Enumerable so all the other standard methods are available.
54
+ #
55
+ # To build a simple VOTable from scratch all in one go one might do this:
56
+ #
57
+ # votable = VOTable.new(
58
+ # :version => '1.1',
59
+ # :coordinate_systems => [
60
+ # Coosys.new(:id => 'J2000', :equinox => 'J2000.', :epoch => 'J2000.', :system => 'eq_FK5')
61
+ # ],
62
+ # :resources => [
63
+ # Resource.new(
64
+ # :name => 'myFavouriteGalaxies',
65
+ # :tables => [
66
+ # Table.new(
67
+ # :name => 'results',
68
+ # :description => Description.new(:text => 'Velocities and Distance estimations'),
69
+ # :params => [
70
+ # Param.new(
71
+ # :name => 'Telescope',
72
+ # :datatype => 'float',
73
+ # :ucd => 'phys.size;instr.tel',
74
+ # :unit => 'm',
75
+ # :value => '3.6'
76
+ # )
77
+ # ],
78
+ # :fields => [
79
+ # Field.new(:name => 'RA', :id => 'col1', :ucd => 'pos.eq.ra;meta.main',
80
+ # :ref => 'J2000', :datatype => 'float', :width => 6, :precision => '2', :unit => 'deg'),
81
+ # Field.new(:name => 'Dec', :id => 'col2', :ucd => 'pos.eq.dec;meta.main',
82
+ # :ref => 'J2000', :datatype => 'float', :width => 6, :precision => '2', :unit => 'deg'),
83
+ # Field.new(:name => 'Name', :id => 'col3', :ucd => 'meta.id;meta.main',
84
+ # :datatype => 'char', :arraysize => '8*'),
85
+ # Field.new(:name => 'RVel', :id => 'col4', :ucd => 'src.veloc.hc', :datatype => 'int',
86
+ # :width => 5, :unit => 'km/s'),
87
+ # Field.new(:name => 'e_RVel', :id => 'col5', :ucd => 'stat.error;src.veloc.hc',
88
+ # :datatype => 'int', :width => 3, :unit => 'km/s'),
89
+ # Field.new(:name => 'R', :id => 'col6', :ucd => 'phys.distance', :datatype => 'float',
90
+ # :width => 4, :precision => '1', :unit => 'Mpc',
91
+ # :description => Description.new(:text => 'Distance of Galaxy, assuming H=75km/s/Mpc'))
92
+ # ],
93
+ # :data => Data.new(
94
+ # :format => TableData.new(
95
+ # :trs => [
96
+ # Tr.new(:tds => [
97
+ # Td.new(:text => '010.68'), Td.new(:text => '+41.27'), Td.new(:text => 'N 224'),
98
+ # Td.new(:text => '-297'), Td.new(:text => '5'), Td.new(:text => '0.7')
99
+ # ]),
100
+ # Tr.new(:tds => [
101
+ # Td.new(:text => '287.43'), Td.new(:text => '-63.85'), Td.new(:text => 'N 6744'),
102
+ # Td.new(:text => '839'), Td.new(:text => '6'), Td.new(:text => '10.4')
103
+ # ]),
104
+ # Tr.new(:tds => [
105
+ # Td.new(:text => '023.48'), Td.new(:text => '+30.66'), Td.new(:text => 'N 598'),
106
+ # Td.new(:text => '-182'), Td.new(:text => '3'), Td.new(:text => '0.7')
107
+ # ])
108
+ # ]
109
+ # )
110
+ # )
111
+ # )
112
+ # ]
113
+ # )
114
+ # ]
115
+ # )
116
+ #
117
+ # Of course, it's more likely you would have read the votable in directly from a file.
118
+ # To convert this votable to a (very) simple CSV-like file you could then:
119
+ #
120
+ # puts votable.resources.first.tables.first.fields.collect{ |field| field.name }.join(',')
121
+ # votable.resources.first.tables.first.data.format.trs.each do |tr|
122
+ # puts tr.tds.collect{ |td| td.text }.join(',')
123
+ # end
124
+ #
125
+ # Which would print out something like:
126
+ #
127
+ # RA,Dec,RVel,e_RVel,R
128
+ # 010.68,+41.27,N 224,-297,5,0.7
129
+ # 287.43,-63.85,N 6744,839,6,10.4
130
+ # 023.48,+30.66,N 598,-182,3,0.7
131
+ #
132
+ # You can also convert the votable to an HTML fragment by doing:
133
+ #
134
+ # votable.to_html
135
+ module VOTable
136
+ # Automatically determines the votable version in question and
137
+ # returns the appropriate domain object.
138
+ # _defn_:: a String, XML::Document, XML::Node or IO object representing the XML.
139
+ #
140
+ # votable = VOTable.from_xml(File.new('votable.basic.xml')) # assuming this is 1.1 document
141
+ # puts votable.class #=> VOTable::V1_1::VOTable
142
+ def self.from_xml(defn)
143
+ generic_vot = Base.new(defn).node
144
+
145
+ namespaces = (generic_vot.namespace || []).collect{ |ns| ns.href }
146
+ namespaces << generic_vot['noNamespaceSchemaLocation']
147
+
148
+ if namespaces.include?('http://www.ivoa.net/xml/VOTable/VOTable/v1.1') or generic_vot['version'] =~ /1\.1$/
149
+ require 'voruby/votable/1.1/votable'
150
+ return VOTable::V1_1::VOTable.new(generic_vot)
151
+ elsif namespaces.include?('http://www.ivoa.net/xml/VOTable/v1.0') or generic_vot['version'] =~ /1\.0$/
152
+ require 'voruby/votable/1.0/votable'
153
+ return VOTable::V1_0::VOTable.new(generic_vot)
154
+ else
155
+ require 'voruby/votable/1.1/votable'
156
+ return VOTable::V1_1::VOTable.new(generic_vot) # go with the most recent if namespace indeterminate
157
+ end
158
+ end
159
+
160
+ # The base class that all VOTable domain objects derive from.
161
+ # You'll never instantiate this directly.
162
+ class Base < XML::Object::Base
163
+ # Get the name of the element when serialized
164
+ # to XML that the object in question will take on.
165
+ def self.element_name
166
+ const_get('ELEMENT_NAME')
167
+ end
168
+
169
+ # Determine the xpath corresponding to the specified class.
170
+ # obj.xpath_for(Field) # => "*[local-name()='FIELD']"
171
+ def xpath_for(klass)
172
+ "*[local-name()='#{klass.element_name}']"
173
+ end
174
+
175
+ # Retrieve the domain object that corresponds to
176
+ # the specified class.
177
+ def get_element(klass)
178
+ subnode = self.node.find_first("*[local-name()='#{klass.element_name}']")
179
+ subnode ? klass.new(subnode) : nil
180
+ end
181
+
182
+ # Equality among domain objects.
183
+ # Two objects are considered equal if they're
184
+ # of the same class and their accessor methods
185
+ # themselves return objects which are equal.
186
+ def ==(obj)
187
+ return false if self.class != obj.class
188
+
189
+ self.methods_to_test_for_equality.each do |method|
190
+ return false if self.send(method) != obj.send(method)
191
+ end
192
+
193
+ true
194
+ end
195
+
196
+ private
197
+
198
+ def initialize_members(args)
199
+ if self.class.respond_to?(:serialization_order)
200
+ self.class.serialization_order.each do |name|
201
+ send("#{name}=", args[name]) if args.has_key?(name)
202
+ end
203
+ else
204
+ args.each do |name, value|
205
+ send("#{name}=", value)
206
+ end
207
+ end
208
+ end
209
+ end
210
+
211
+ # Some helper methods for converting string values that have an associated
212
+ # data type (like TDs do through their associated FIELD).
213
+ module Castable
214
+ # Convert a string value into its corresponding basic object representation,
215
+ # if appropriate. This does *not* work on array values, only on scalars.
216
+ # Native ruby types map to votable datatypes in the following way:
217
+ #
218
+ # * 'boolean' => boolean (i.e TrueClass or FalseClass)
219
+ # * 'bit' => Integer (1 or 0)
220
+ # * 'unsignedByte' => Integer representation of the byte
221
+ # * 'short, 'int', 'long' => Integer
222
+ # * 'char', 'unicodeChar' => String
223
+ # * 'float', 'double' => Float or Bignum
224
+ # * 'floatComplex', 'doubleComplex' => Complex
225
+ def cast(txt, to='char')
226
+ case to
227
+ when 'boolean'
228
+ txt == '1' || txt.downcase == 'true' || txt.downcase == 't'
229
+ when 'bit' then txt.to_i
230
+ when 'unsignedByte' then txt[0]
231
+ when 'short' then txt.to_i
232
+ when 'int' then txt.to_i
233
+ when 'long' then txt.to_i
234
+ when 'double' then txt.to_f
235
+ when 'float' then txt.to_f
236
+ else
237
+ txt
238
+ end
239
+ end
240
+
241
+ # Serialize a ruby object to the format that is correct
242
+ # for a votable.
243
+ def convert_to_s(val, from='char')
244
+ case from
245
+ when 'boolean'
246
+ (val.is_a?(TrueClass) or val.to_s.downcase =~ /^t/ or val.to_s == '1') ?
247
+ 'true' : 'false'
248
+ when 'bit' then val.to_i.to_s
249
+ when 'short' then val.to_i.to_s
250
+ when 'int' then val.to_i.to_s
251
+ when 'long' then val.to_i.to_s
252
+ when 'float' then val.to_f.to_s
253
+ when 'double' then val.to_f.to_s
254
+ when 'floatComplex' then "#{val.real} #{val.image}"
255
+ when 'doubleComplex' then "#{val.real} #{val.image}"
256
+ else
257
+ val.to_s
258
+ end
259
+ end
260
+
261
+ # Much like #cast but understands arrays.
262
+ def as_obj(txt, datatype, arraysize=nil)
263
+ raise "Unable to determine datatype" if !datatype
264
+
265
+ txt_list = arraysize ? txt.split(/\s+/) : [txt]
266
+ values_list = []
267
+
268
+ # character string, do nothing
269
+ return txt if datatype == 'char' or datatype == 'unicodeChar'
270
+
271
+ # complex values are a bit different
272
+ if datatype == 'floatComplex' || datatype == 'doubleComplex'
273
+ txt_list = txt.split(/\s+/)
274
+ complex_numbers = []
275
+ i = 0
276
+ (0...txt_list.size/2).each do |n|
277
+ complex_numbers << Complex.new(txt_list[i].to_f, txt_list[i+1].to_f)
278
+ i += 2
279
+ end
280
+ return arraysize ? complex_numbers : complex_numbers.first
281
+ end
282
+
283
+ txt_list.each do |component|
284
+ values_list << cast(component, datatype)
285
+ end
286
+
287
+ arraysize ? values_list : values_list.first
288
+ end
289
+
290
+ # Much like #convert_to_s but understands arrays.
291
+ def as_string(obj, datatype, arraysize=nil)
292
+ raise "Unable to determine datatype" if !datatype
293
+ raise "Supposed to contain an array of size '#{arraysize}'" if arraysize and !obj.is_a?(Array)
294
+ raise "Supposed to contain a single value" if !arraysize and obj.is_a?(Array)
295
+
296
+ obj = [obj] if !obj.is_a?(Array)
297
+
298
+ values = []
299
+ obj.each do |val|
300
+ values << convert_to_s(val, datatype)
301
+ end
302
+
303
+ values.join(' ')
304
+ end
305
+ end
306
+
307
+ end
308
+ end
@@ -0,0 +1,491 @@
1
+ require 'base64'
2
+ require 'open-uri'
3
+
4
+ begin
5
+ require 'rubygems'
6
+ rescue LoadError; end
7
+
8
+ gem 'soap4r'
9
+ require 'soap/wsdlDriver'
10
+
11
+ require 'voruby/votable/1.1/votable'
12
+
13
+ module VORuby
14
+ module Wesix
15
+ class SextractorParams
16
+ WESIX_NS = 'http://wesixtest.phyast.pitt.edu'
17
+
18
+ def initialize(params={})
19
+ params.each{ |key, value| self.send("#{key}=", value) }
20
+ end
21
+ end
22
+
23
+ # Object containing the parameters to be used by WESIX (e.g. SExtractor) to create the source catalog.
24
+ # You can preset values by passing a hash to the constructor:
25
+ #
26
+ # params = Params.new(:analysis_thresh => 2.0)
27
+ # params.detect_thresh = 2.0
28
+ # puts params.analysis_thresh # => 2.0
29
+ #
30
+ # Available methods are:
31
+ # #analysis_thresh, #backphoto_type, #back_filtersize, #back_size, #catalog_name, #catalog_type,
32
+ # #checkimage_name, #checkimage_type, #clean, #clean_param, #deblend_mincont, #deblend_nthresh,
33
+ # #detect_minarea, #detect_thresh, #detect_type, #filter, #filter_name, #flag_image, #gain,
34
+ # #mag_gamma, #mag_zeropoint, #mask_type, #memory_bufsize, #memory_objstack, #memory_pixstack,
35
+ # #parameters_name, #phot_apertures, #phot_autoparams1, #phot_autoparams2, #pixel_scale, #satur_level,
36
+ # #seeing_fwhm, #starnnw_name, #verbose_type, #query1, #query2, #string
37
+ class Params < SextractorParams
38
+ # Returns the schema required to convert this object into a
39
+ # fully-fledged SOAP object. Used by Service.
40
+ def self.soap_registration
41
+ {
42
+ :class => self,
43
+ :schema_type => XSD::QName.new(WESIX_NS, "SexParams"),
44
+ :schema_element => [
45
+ ["analysis_thresh", ["SOAP::SOAPDouble", XSD::QName.new(nil, "ANALYSIS_THRESH")]],
46
+ ["backphoto_type", ["SOAP::SOAPString", XSD::QName.new(nil, "BACKPHOTO_TYPE")]],
47
+ ["back_filtersize", ["SOAP::SOAPInt", XSD::QName.new(nil, "BACK_FILTERSIZE")]],
48
+ ["back_size", ["SOAP::SOAPInt", XSD::QName.new(nil, "BACK_SIZE")]],
49
+ ["catalog_name", ["SOAP::SOAPString", XSD::QName.new(nil, "CATALOG_NAME")]],
50
+ ["catalog_type", ["SOAP::SOAPString", XSD::QName.new(nil, "CATALOG_TYPE")]],
51
+ ["checkimage_name", ["SOAP::SOAPString", XSD::QName.new(nil, "CHECKIMAGE_NAME")]],
52
+ ["checkimage_type", ["SOAP::SOAPString", XSD::QName.new(nil, "CHECKIMAGE_TYPE")]],
53
+ ["clean", ["SOAP::SOAPString", XSD::QName.new(nil, "CLEAN")]],
54
+ ["clean_param", ["SOAP::SOAPDouble", XSD::QName.new(nil, "CLEAN_PARAM")]],
55
+ ["deblend_mincont", ["SOAP::SOAPDouble", XSD::QName.new(nil, "DEBLEND_MINCONT")]],
56
+ ["deblend_nthresh", ["SOAP::SOAPInt", XSD::QName.new(nil, "DEBLEND_NTHRESH")]],
57
+ ["detect_minarea", ["SOAP::SOAPInt", XSD::QName.new(nil, "DETECT_MINAREA")]],
58
+ ["detect_thresh", ["SOAP::SOAPDouble", XSD::QName.new(nil, "DETECT_THRESH")]],
59
+ ["detect_type", ["SOAP::SOAPString", XSD::QName.new(nil, "DETECT_TYPE")]],
60
+ ["filter", ["SOAP::SOAPString", XSD::QName.new(nil, "FILTER")]],
61
+ ["filter_name", ["SOAP::SOAPString", XSD::QName.new(nil, "FILTER_NAME")]],
62
+ ["flag_image", ["SOAP::SOAPString", XSD::QName.new(nil, "FLAG_IMAGE")]],
63
+ ["gain", ["SOAP::SOAPDouble", XSD::QName.new(nil, "GAIN")]],
64
+ ["mag_gamma", ["SOAP::SOAPDouble", XSD::QName.new(nil, "MAG_GAMMA")]],
65
+ ["mag_zeropoint", ["SOAP::SOAPDouble", XSD::QName.new(nil, "MAG_ZEROPOINT")]],
66
+ ["mask_type", ["SOAP::SOAPString", XSD::QName.new(nil, "MASK_TYPE")]],
67
+ ["memory_bufsize", ["SOAP::SOAPInt", XSD::QName.new(nil, "MEMORY_BUFSIZE")]],
68
+ ["memory_objstack", ["SOAP::SOAPInt", XSD::QName.new(nil, "MEMORY_OBJSTACK")]],
69
+ ["memory_pixstack", ["SOAP::SOAPInt", XSD::QName.new(nil, "MEMORY_PIXSTACK")]],
70
+ ["parameters_name", ["SOAP::SOAPString", XSD::QName.new(nil, "PARAMETERS_NAME")]],
71
+ ["phot_apertures", ["SOAP::SOAPInt", XSD::QName.new(nil, "PHOT_APERTURES")]],
72
+ ["phot_autoparams1", ["SOAP::SOAPDouble", XSD::QName.new(nil, "PHOT_AUTOPARAMS1")]],
73
+ ["phot_autoparams2", ["SOAP::SOAPDouble", XSD::QName.new(nil, "PHOT_AUTOPARAMS2")]],
74
+ ["pixel_scale", ["SOAP::SOAPDouble", XSD::QName.new(nil, "PIXEL_SCALE")]],
75
+ ["satur_level", ["SOAP::SOAPDouble", XSD::QName.new(nil, "SATUR_LEVEL")]],
76
+ ["seeing_fwhm", ["SOAP::SOAPDouble", XSD::QName.new(nil, "SEEING_FWHM")]],
77
+ ["starnnw_name", ["SOAP::SOAPString", XSD::QName.new(nil, "STARNNW_NAME")]],
78
+ ["verbose_type", ["SOAP::SOAPString", XSD::QName.new(nil, "VERBOSE_TYPE")]],
79
+ ["query1", ["SOAP::SOAPString", XSD::QName.new(nil, "query1")]],
80
+ ["query2", ["SOAP::SOAPString", XSD::QName.new(nil, "query2")]],
81
+ ["string", ["SOAP::SOAPString", XSD::QName.new(nil, "string")]]
82
+ ]
83
+ }
84
+ end
85
+
86
+ attr_accessor *soap_registration[:schema_element].collect{ |defn| defn.first }
87
+ end
88
+
89
+ # Object containing the measured parameters (all booleans) to be included in the output catalog
90
+ # generated by WESIX. You can preset values by passing a hash to the constructor:
91
+ #
92
+ # params = OutputParams.new(:flux_best => true)
93
+ # params.fluxerr_best = true
94
+ # puts params.flux_best # => true
95
+ #
96
+ # Available methods are:
97
+ # #alpha_b1950, #alpha_j2000, #alpha_sky, #background, #class_star, #cxx_image, #cxx_world,
98
+ # #cxy_image, #cxy_world, #cyy_image, #cyy_world, #delta_b1950, #delta_j2000, #delta_sky,
99
+ # #ellipticity, #elongation, #erra_image, #erra_world, #errb_image, #errb_world, #errcxx_image,
100
+ # #errcxx_world, #errcxy_image, #errcxy_world, #errcyy_image, #errcyy_world, #errtheta_b1950,
101
+ # #errtheta_image, #errtheta_j2000, #errtheta_sky, #errtheta_world, #errx2_image, #errx2_world,
102
+ # #errxy_image, #errxy_world, #erry2_image, #erry2_world, #flags, #fluxerr_aper, #fluxerr_auto,
103
+ # #fluxerr_best, #fluxerr_iso, #fluxerr_isocor, #flux_aper, #flux_auto, #flux_best, #flux_iso,
104
+ # #flux_isocor, #flux_max, #fwhm_image, #fwhm_world, #imaflags_iso, #iso0, #iso1, #iso2, #iso3,
105
+ # #iso4, #iso5, #iso6, #iso7, #isoarea_image, #isoarea_world, #kron_radius, #magerr_aper, #magerr_auto,
106
+ # #magerr_best, #magerr_iso, #magerr_isocor, #mag_aper, #mag_auto, #mag_best, #mag_iso, #mag_isocor, #mu_max,
107
+ # #mu_threshold, #nimaflags_iso, #number, #theta_b1950, #theta_image, #theta_j2000, #theta_sky, #theta_world,
108
+ # #threshold, #vignet, #xmax_image, #xmin_image, #xy_image, #xy_world, #ymax_image, #ymin_image, #a_image, #a_world,
109
+ # #b_image, #b_world, #params, #x2_image, #x2_world, #x_image, #x_world, #y2_image, #y2_world, #y_image, #y_world
110
+ class OutputParams < SextractorParams
111
+ # Returns the schema required to convert this object into a
112
+ # fully-fledged SOAP object. Used by Service.
113
+ def self.soap_registration
114
+ {
115
+ :class => self,
116
+ :schema_type => XSD::QName.new(WESIX_NS, "SexOutputParams"),
117
+ :schema_element => [
118
+ ["alpha_b1950", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ALPHA_B1950")]],
119
+ ["alpha_j2000", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ALPHA_J2000")]],
120
+ ["alpha_sky", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ALPHA_SKY")]],
121
+ ["background", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "BACKGROUND")]],
122
+ ["class_star", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "CLASS_STAR")]],
123
+ ["cxx_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "CXX_IMAGE")]],
124
+ ["cxx_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "CXX_WORLD")]],
125
+ ["cxy_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "CXY_IMAGE")]],
126
+ ["cxy_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "CXY_WORLD")]],
127
+ ["cyy_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "CYY_IMAGE")]],
128
+ ["cyy_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "CYY_WORLD")]],
129
+ ["delta_b1950", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "DELTA_B1950")]],
130
+ ["delta_j2000", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "DELTA_J2000")]],
131
+ ["delta_sky", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "DELTA_SKY")]],
132
+ ["ellipticity", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ELLIPTICITY")]],
133
+ ["elongation", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ELONGATION")]],
134
+ ["erra_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRA_IMAGE")]],
135
+ ["erra_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRA_WORLD")]],
136
+ ["errb_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRB_IMAGE")]],
137
+ ["errb_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRB_WORLD")]],
138
+ ["errcxx_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRCXX_IMAGE")]],
139
+ ["errcxx_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRCXX_WORLD")]],
140
+ ["errcxy_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRCXY_IMAGE")]],
141
+ ["errcxy_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRCXY_WORLD")]],
142
+ ["errcyy_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRCYY_IMAGE")]],
143
+ ["errcyy_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRCYY_WORLD")]],
144
+ ["errtheta_b1950", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRTHETA_B1950")]],
145
+ ["errtheta_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRTHETA_IMAGE")]],
146
+ ["errtheta_j2000", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRTHETA_J2000")]],
147
+ ["errtheta_sky", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRTHETA_SKY")]],
148
+ ["errtheta_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRTHETA_WORLD")]],
149
+ ["errx2_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRX2_IMAGE")]],
150
+ ["errx2_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRX2_WORLD")]],
151
+ ["errxy_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRXY_IMAGE")]],
152
+ ["errxy_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRXY_WORLD")]],
153
+ ["erry2_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRY2_IMAGE")]],
154
+ ["erry2_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ERRY2_WORLD")]],
155
+ ["flags", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "FLAGS")]],
156
+ ["fluxerr_aper", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "FLUXERR_APER")]],
157
+ ["fluxerr_auto", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "FLUXERR_AUTO")]],
158
+ ["fluxerr_best", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "FLUXERR_BEST")]],
159
+ ["fluxerr_iso", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "FLUXERR_ISO")]],
160
+ ["fluxerr_isocor", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "FLUXERR_ISOCOR")]],
161
+ ["flux_aper", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "FLUX_APER")]],
162
+ ["flux_auto", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "FLUX_AUTO")]],
163
+ ["flux_best", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "FLUX_BEST")]],
164
+ ["flux_iso", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "FLUX_ISO")]],
165
+ ["flux_isocor", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "FLUX_ISOCOR")]],
166
+ ["flux_max", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "FLUX_MAX")]],
167
+ ["fwhm_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "FWHM_IMAGE")]],
168
+ ["fwhm_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "FWHM_WORLD")]],
169
+ ["imaflags_iso", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "IMAFLAGS_ISO")]],
170
+ ["iso0", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ISO0")]],
171
+ ["iso1", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ISO1")]],
172
+ ["iso2", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ISO2")]],
173
+ ["iso3", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ISO3")]],
174
+ ["iso4", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ISO4")]],
175
+ ["iso5", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ISO5")]],
176
+ ["iso6", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ISO6")]],
177
+ ["iso7", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ISO7")]],
178
+ ["isoarea_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ISOAREA_IMAGE")]],
179
+ ["isoarea_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "ISOAREA_WORLD")]],
180
+ ["kron_radius", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "KRON_RADIUS")]],
181
+ ["magerr_aper", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "MAGERR_APER")]],
182
+ ["magerr_auto", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "MAGERR_AUTO")]],
183
+ ["magerr_best", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "MAGERR_BEST")]],
184
+ ["magerr_iso", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "MAGERR_ISO")]],
185
+ ["magerr_isocor", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "MAGERR_ISOCOR")]],
186
+ ["mag_aper", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "MAG_APER")]],
187
+ ["mag_auto", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "MAG_AUTO")]],
188
+ ["mag_best", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "MAG_BEST")]],
189
+ ["mag_iso", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "MAG_ISO")]],
190
+ ["mag_isocor", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "MAG_ISOCOR")]],
191
+ ["mu_max", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "MU_MAX")]],
192
+ ["mu_threshold", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "MU_THRESHOLD")]],
193
+ ["nimaflags_iso", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "NIMAFLAGS_ISO")]],
194
+ ["number", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "NUMBER")]],
195
+ ["theta_b1950", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "THETA_B1950")]],
196
+ ["theta_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "THETA_IMAGE")]],
197
+ ["theta_j2000", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "THETA_J2000")]],
198
+ ["theta_sky", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "THETA_SKY")]],
199
+ ["theta_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "THETA_WORLD")]],
200
+ ["threshold", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "THRESHOLD")]],
201
+ ["vignet", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "VIGNET")]],
202
+ ["xmax_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "XMAX_IMAGE")]],
203
+ ["xmin_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "XMIN_IMAGE")]],
204
+ ["xy_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "XY_IMAGE")]],
205
+ ["xy_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "XY_WORLD")]],
206
+ ["ymax_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "YMAX_IMAGE")]],
207
+ ["ymin_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "YMIN_IMAGE")]],
208
+ ["a_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "a_IMAGE")]],
209
+ ["a_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "a_WORLD")]],
210
+ ["b_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "b_IMAGE")]],
211
+ ["b_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "b_WORLD")]],
212
+ ["params", ["SOAP::SOAPString", XSD::QName.new(nil, "params")]],
213
+ ["x2_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "x2_IMAGE")]],
214
+ ["x2_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "x2_WORLD")]],
215
+ ["x_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "x_IMAGE")]],
216
+ ["x_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "x_WORLD")]],
217
+ ["y2_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "y2_IMAGE")]],
218
+ ["y2_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "y2_WORLD")]],
219
+ ["y_image", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "y_IMAGE")]],
220
+ ["y_world", ["SOAP::SOAPBoolean", XSD::QName.new(nil, "y_WORLD")]]
221
+ ]
222
+ }
223
+ end
224
+
225
+ attr_accessor *soap_registration[:schema_element].collect{ |defn| defn.first }
226
+ end
227
+
228
+ # Allows access to WESIX[http://nvogre.phyast.pitt.edu:8080/wesix/],
229
+ # a webservice interface to the standard astronomical image analysis package SExtractor
230
+ # together with a cross matching service using OpenSkyQuery[http://openskyquery.net/].
231
+ #
232
+ # Supposing the FITS file you're interested in is available via a URL, to run a
233
+ # source extraction with sensible defaults:
234
+ #
235
+ # wesix = Service.new
236
+ # votable = wesix.extract('http://nvogre.phyast.pitt.edu:8080/wesix/testr.fits')
237
+ #
238
+ # If it happens to be on disk, you can:
239
+ #
240
+ # votable = wesix.extract(File.new('myfits.fits'))
241
+ #
242
+ # The returned object is a standard VOTable::V1_1::VOTable from which your sources can
243
+ # be extracted.
244
+ #
245
+ # Likewise, an extraction and crossmatch against the SDSS could be done in the following way:
246
+ #
247
+ # votable = wesix.xmatch('http://nvogre.phyast.pitt.edu:8080/wesix/testr.fits')
248
+ #
249
+ # or if it's on disk:
250
+ #
251
+ # votable = wesix.xmatch(File.new('myfits.fits'))
252
+ class Service
253
+ attr_reader :end_point
254
+
255
+ # Convenience method for Service#extract.
256
+ # votable = Service.extract('http://nvogre.phyast.pitt.edu:8080/wesix/testr.fits')
257
+ def self.extract(resource, transpose=false, *args)
258
+ wesix = Service.new
259
+ wesix.extract(resource, transpose, *args)
260
+ end
261
+
262
+ # Convenience method for Service#xmatch.
263
+ # votable = Service.xmatch('http://nvogre.phyast.pitt.edu:8080/wesix/testr.fits')
264
+ def self.xmatch(resource, transpose=false, *args)
265
+ wesix = Service.new
266
+ wesix.xmatch(resource, transpose, *args)
267
+ end
268
+
269
+ # Create a new wesix service instance.
270
+ # Currently the only valid key in the <tt>options</tt> hash is <tt>:end_point</tt>
271
+ # representing the location of the WESIX soap service, and defaults to
272
+ # http://nvogre.phyast.pitt.edu:8080/axis/services/WesixTest. Since at the moment this is
273
+ # the only existing instance of WESIX, you'll never need to specify it.
274
+ #
275
+ # wesix = Wesix.new
276
+ def initialize(options={})
277
+ self.end_point = options[:end_point] || 'http://nvogre.phyast.pitt.edu:8080/axis/services/WesixTest'
278
+ end
279
+
280
+ # Set the locaton of the WESIX webservice.
281
+ # In practice, you'll never need to use this.
282
+ def end_point=(end_point)
283
+ @end_point = end_point
284
+
285
+ # Create the driver.
286
+ # We don't use the WSDL since we won't expose all possible methods.
287
+ @extractor = SOAP::RPC::Driver.new(self.end_point)
288
+ #@extractor.wiredump_dev = STDOUT
289
+ @extractor.return_response_as_xml = true # in prep for dumping the result into a VORuby::VOTable
290
+
291
+ # Map the appropriate objects.
292
+ map = SOAP::Mapping::Registry.new
293
+ map.register(Params.soap_registration)
294
+ map.register(OutputParams.soap_registration)
295
+ @extractor.mapping_registry = map
296
+
297
+ # Define methods.
298
+ @extractor.add_method('wsextractor1VO', 'in0', 'in1')
299
+ @extractor.add_method('wsextractor1VOXmatch', 'in0', 'in1')
300
+ @extractor.add_method('wsextractorURL1VO', 'in0', 'in1')
301
+ @extractor.add_method('wsextractorURL1VOXmatch', 'in0', 'in1')
302
+
303
+ @extractor.add_method('wsextractor2VO', 'in0', 'in1', 'in2')
304
+ @extractor.add_method('wsextractor2VOXmatch', 'in0', 'in1', 'in2')
305
+ @extractor.add_method('wsextractorURL2VO', 'in0', 'in1', 'in2')
306
+ @extractor.add_method('wsextractorURL2VOXmatch', 'in0', 'in1', 'in2')
307
+
308
+ @extractor.add_method('wsextractor3VO', 'in0', 'in1', 'in2', 'in3')
309
+ @extractor.add_method('wsextractor3VOXmatch', 'in0', 'in1', 'in2', 'in3')
310
+ @extractor.add_method('wsextractorURL3VO', 'in0', 'in1', 'in2', 'in3')
311
+ @extractor.add_method('wsextractorURL3VOXmatch', 'in0', 'in1', 'in2', 'in3')
312
+ end
313
+
314
+ # Perform a source extraction on the specified FITS file.
315
+ #
316
+ # <tt>resource</tt> is the FITS file in question. It may be string representing a URL,
317
+ # a URI object or an object that responds to #read (such as a File).
318
+ #
319
+ # <tt>transpose</tt> is a boolean value indicating whether to transpose the image before SExtraction
320
+ # (sometimes necessary for SDSS images).
321
+ #
322
+ # The next two optional arguments are the parameters
323
+ # to be used by SExtractor to create the source catalog (as a Params object) and
324
+ # the measured parameters to be included in the output catalog (as an OutputParams object).
325
+ #
326
+ # A VOTable::V1_1::VOTable is returned.
327
+ #
328
+ # ===== Source extraction with defaults
329
+ #
330
+ # wesix.extract('http://nvogre.phyast.pitt.edu:8080/wesix/testr.fits') # from a URL
331
+ # wesix.extract(URL.parse('http://nvogre.phyast.pitt.edu:8080/wesix/testr.fits')) # this is the same as above
332
+ # wesix.extract(File.new('../fits/my_file.fits')) # from a local file
333
+ #
334
+ # ===== Source extraction with sextractor parameters defined
335
+ #
336
+ # wesix.extract(
337
+ # File.new('../fits/my_file.fits'), # local file, pass a string or URI object if the FITS file is remote
338
+ # false, # no transposition
339
+ # Params.new(:detect_thresh => 2.0, :analysis_thresh => 2.0)
340
+ # )
341
+ #
342
+ # ===== Source extraction with sextractor parameters and measured parameters defined
343
+ #
344
+ # wesix.extract(
345
+ # File.new('../fits/my_file.fits'), # local file, pass a string or URI object if the FITS file is remote
346
+ # false, # no transposition
347
+ # Params.new(:detect_thresh => 2.0, :analysis_thresh => 2.0),
348
+ # OutputParams.new(:flux_best => true, :fluxerr_best => true)
349
+ # )
350
+ def extract(resource, transpose=false, *args)
351
+ if resource.is_a?(URI) or resource.is_a?(String) # FITS file is on a remote machine
352
+ self.extract_from_url(resource.to_s, transpose, *args)
353
+ elsif resource.respond_to?(:read) # FITS file is on a local machine, stored as a diskfile
354
+ self.extract_from_file(resource, transpose, *args)
355
+ else
356
+ raise ArgumentError, 'resource must be a URI, String or respond to a method called #read'
357
+ end
358
+ end
359
+
360
+ # Alias for #extract_and_xmatch
361
+ def xmatch(resource, transpose=false, *args)
362
+ self.extract_and_xmatch(resource, transpose, *args)
363
+ end
364
+
365
+ # Perform a source extraction followed by a crossmatch on the specified FITS file.
366
+ #
367
+ # <tt>resource</tt> is the FITS file in question. It may be string representing a URL,
368
+ # a URI object or an object that responds to #read (such as a File).
369
+ #
370
+ # <tt>transpose</tt> is a boolean value indicating whether to transpose the image before SExtraction
371
+ # (sometimes necessary for SDSS images).
372
+ #
373
+ # The next two optional arguments are the parameters
374
+ # to be used by SExtractor to create the source catalog (as a Params object) and
375
+ # the measured parameters to be included in the output catalog (as an OutputParams object).
376
+ #
377
+ # A VOTable::V1_1::VOTable is returned.
378
+ #
379
+ # ===== Source extraction and crossmatch with defaults
380
+ #
381
+ # wesix.extract_and_xmatch('http://nvogre.phyast.pitt.edu:8080/wesix/testr.fits') # from a URL
382
+ # wesix.extract_and_xmatch(URL.parse('http://nvogre.phyast.pitt.edu:8080/wesix/testr.fits')) # this is the same as above
383
+ # wesix.extract_and_xmatch(File.new('../fits/my_file.fits')) # from a local file
384
+ #
385
+ # ===== Source extraction and crossmatch with sextractor parameters defined
386
+ #
387
+ # wesix.extract_and_xmatch(
388
+ # File.new('../fits/my_file.fits'), # local file, pass a string or URI object if the FITS file is remote
389
+ # false, # no transposition
390
+ # Params.new(:detect_thresh => 2.0, :analysis_thresh => 2.0)
391
+ # )
392
+ #
393
+ # ===== Source extraction and crossmatch with sextractor parameters and measured parameters defined
394
+ #
395
+ # wesix.extract_and_xmatch(
396
+ # File.new('../fits/my_file.fits'), # local file, pass a string or URI object if the FITS file is remote
397
+ # false, # no transposition
398
+ # Params.new(:detect_thresh => 2.0, :analysis_thresh => 2.0),
399
+ # OutputParams.new(:flux_best => true, :fluxerr_best => true)
400
+ # )
401
+ def extract_and_xmatch(resource, transpose=false, *args)
402
+ if resource.is_a?(URI) or resource.is_a?(String) # FITS file is on a remote machine
403
+ self.extract_and_xmatch_from_url(resource.to_s, transpose, *args)
404
+ elsif resource.respond_to?(:read) # FITS file is on a local machine, stored as a diskfile
405
+ self.extract_and_xmatch_from_file(resource, transpose, *args)
406
+ else
407
+ raise ArgumentError, 'resource must be a URI, String or respond to a method called #read'
408
+ end
409
+ end
410
+
411
+ # Perform a source extraction on a local FITS file.
412
+ # <tt>file</tt> is any object with a #read method (such as File).
413
+ # Otherwise exactly as #extract.
414
+ def extract_from_file(file, transpose=false, *args)
415
+ raise "resource does not respond to #read" if !file.respond_to?(:read)
416
+
417
+ file = SOAP::SOAPBase64.new(file.read)
418
+ file.type = XSD::QName.new('http://www.w3.org/2001/XMLSchema', 'base64Binary')
419
+ transpose = transpose ? 1 : 0
420
+
421
+ response = case args.size
422
+ when 0 then @extractor.wsextractor1VO(file, transpose) # basic
423
+ when 1 then @extractor.wsextractor2VO(file, args.first, transpose) # input params specified
424
+ when 2 then @extractor.wsextractor3VO(file, args.first, args.last, transpose) # input params/output flags specified
425
+ else
426
+ raise ArgumentError, "wrong number of optional arguments: #{args.size} > 2"
427
+ end
428
+
429
+ vot_node = XML::Parser.string(response).parse.root.find_first("//*[local-name()='VOTABLE']")
430
+ VORuby::VOTable.from_xml(vot_node)
431
+ end
432
+
433
+ # Perform a source extraction and crossmatch on a local FITS file.
434
+ # <tt>file</tt> is any object with a #read method (such as File).
435
+ # Otherwise exactly as #extract_and_xmatch.
436
+ def extract_and_xmatch_from_file(file, transpose=false, *args)
437
+ raise "resource does not respond to #read" if !file.respond_to?(:read)
438
+
439
+ file = SOAP::SOAPBase64.new(file.read)
440
+ file.type = XSD::QName.new('http://www.w3.org/2001/XMLSchema', 'base64Binary')
441
+ transpose = transpose ? 1 : 0
442
+
443
+ response = case args.size
444
+ when 0 then @extractor.wsextractor1VOXmatch(file, transpose) # basic
445
+ when 1 then @extractor.wsextractor2VOXmatch(file, args.first, transpose) # input params specified
446
+ when 2 then @extractor.wsextractor3VOXmatch(file, args.first, args.last, transpose) # input params/output flags specified
447
+ else
448
+ raise ArgumentError, "wrong number of optional arguments: #{args.size} > 2"
449
+ end
450
+
451
+ vot_node = XML::Parser.string(response).parse.root.find_first("//*[local-name()='VOTABLE']")
452
+ VORuby::VOTable.from_xml(vot_node)
453
+ end
454
+
455
+ # Perform a source extraction on a remotely accessible FITS file.
456
+ # <tt>url</tt> may be a string or a URI object. Otherwise exactly as for #extract.
457
+ def extract_from_url(url, transpose=false, *args)
458
+ transpose = transpose ? 1 : 0
459
+
460
+ response = case args.size
461
+ when 0 then @extractor.wsextractorURL1VO(url.to_s, transpose) # basic
462
+ when 1 then @extractor.wsextractorURL2VO(url.to_s, args.first, transpose) # input params specified
463
+ when 2 then @extractor.wsextractorURL3VO(url.to_s, args.first, args.last, transpose) # input params/output flags specified
464
+ else
465
+ raise ArgumentError, "wrong number of optional arguments: #{args.size} > 2"
466
+ end
467
+
468
+ vot_node = XML::Parser.string(response).parse.root.find_first("//*[local-name()='VOTABLE']")
469
+ VORuby::VOTable.from_xml(vot_node)
470
+ end
471
+
472
+ # Perform a source extraction and crossmatch on a remotely accessible FITS file.
473
+ # <tt>url</tt> may be a string or a URI object. Otherwise exactly as for #extract_and_xmatch.
474
+ def extract_and_xmatch_from_url(url, transpose=false, *args)
475
+ transpose = transpose ? 1 : 0
476
+
477
+ response = case args.size
478
+ when 0 then @extractor.wsextractorURL1VOXmatch(url.to_s, transpose) # basic
479
+ when 1 then @extractor.wsextractorURL2VOXmatch(url.to_s, args.first, transpose) # input params specified
480
+ when 2 then @extractor.wsextractorURL3VOXmatch(url.to_s, args.first, args.last, transpose) # input params/output flags specified
481
+ else
482
+ raise ArgumentError, "wrong number of optional arguments: #{args.size} > 2"
483
+ end
484
+
485
+ vot_node = XML::Parser.string(response).parse.root.find_first("//*[local-name()='VOTABLE']")
486
+ VORuby::VOTable.from_xml(vot_node)
487
+ end
488
+ end
489
+
490
+ end
491
+ end