require 'voruby/votable/votable' module VORuby module VOTable # Classes that represent the domain objects in the VOTable 1.0 specification # (http://www.ivoa.net/Documents/PR/VOTable/VOTable-20031017.html). module V1_0 # The root element of the votable. class VOTable < VORuby::VOTable::Base ELEMENT_NAME = 'VOTABLE' def self.serialization_order [ :description, :definitions, :infos, :resources, :version, :id ] end # Create a votable. # To parse an existing votable one would normally do this: # votable = VOTable.from_file('my_file.xml') # but any of the other methods described in VOTable::Base#new are also acceptable: # votable = VOTable.new( # :version => '1.0', # :id => 'votable1', # :description => Description.new(:text => 'A test votable'), # :resources => [Resource.new(...)] # ) # etc. # # or to create a blank VOTable that you'll build up later: # votable = VOTable.new() def initialize(defn=nil) super(defn) self.version = '1.0' if !self.version end # Retrieve the version. By default this is '1.0'. def version self.node['version'] end # Set the version. Normally you should leave # this as 1.0. def version=(v) @node['version'] = v.to_s end def id self.node['ID'] end def id=(i) @node['ID'] = i.to_s end # Retrieve the description (Description). def description get_element(Description) end # Set the description (Description). # votable.description = Description.new(:text => 'A fascinating votable') def description=(d) set_element(Description, d) end # Retrieve the definitions (Definitions). def definitions get_element(Definitions) end # Set the definitions (Definitions). def definitions=(d) set_element(Definitions, d) end # Retrieve the coordinate systems (Coosys). def coordinate_systems HomogeneousNodeList.new(self.node, xpath_for(Coosys), Coosys) end # Set the coordinate systems. # Takes an array of Coosys objects. # votable.coordinate_systems = [Coosys.new(), ...] def coordinate_systems=(systems) self.coordinate_systems.replace(systems) end # Retrieve the parameters (Param). # Returns a HomogeneousNodeList. def params HomogeneousNodeList.new(self.node, xpath_for(Param), Param) end # Set the parameters. # Takes an array of Param objects. # votable.params = [Param.new(), ...] def params=(parameters) self.params.replace(parameters) end # Retrieve the information list (Info). # Returns a HomogeneousNodeList. def infos HomogeneousNodeList.new(self.node, xpath_for(Info), Info) end # Set the information list. # Takes an array of Info objects. # votable.infos = [Info.new(), ...] def infos=(is) self.infos.replace(is) end # Retrieve the resources (Resource). # Returns a HomogeneousNodeList. def resources HomogeneousNodeList.new(self.node, xpath_for(Resource), Resource) end # Set the resources. # Takes an array of Resource objects. # votable.resources = [Resource.new(), ...] def resources=(res) self.resources.replace(res) end # Convert a VOTable to HTML. # Not all attributes are converted. # See test/voruby/votable/1.1/votable.html for an example of the output. def to_html builder = Builder::XmlMarkup.new(:indent => 2) votable_opts = {:class => 'votable'} votable_opts[:id] = self.id if self.id builder.div(votable_opts){ |votable| votable.div(self.description.text, :class => 'description') if self.description self.resources.each do |res| votable << res.to_html end } end protected def methods_to_test_for_equality [ :version, :id, :description, :definitions, :infos, :resources ] end end # A simple description of something. class Description < VORuby::VOTable::Base ELEMENT_NAME = 'DESCRIPTION' # Create a description. # descr = Description.new(:text => 'Hello, world!') def initialize(defn=nil) super(defn) end # Retrieve the actual text of the description. def text self.node.content end # Set the text of the description. # descr.text = 'Hello, world!' def text=(text) @node.content = text end protected def methods_to_test_for_equality; [:text] end end # Definitions of various values appropriate to the votable. class Definitions < VORuby::VOTable::Base ELEMENT_NAME = 'DEFINITIONS' # Create a set of definitions. # definitions = Definitions.new( # :coordinate_systems => [Coosys.new] # :params => [Param.new] # ) def initialize(defn=nil) super(defn) end # Retrieve the coordinate systems (Coosys). # Returns a HomogeneousNodeList. def coordinate_systems HomogeneousNodeList.new(self.node, xpath_for(Coosys), Coosys) end # Retrieve the parameters (Param). # Returns a HomogeneousNodeList. def coordinate_systems=(systems) self.coordinate_systems.replace(systems) end # Retrieve the parameters (Param). # Returns a HomogeneousNodeList. def params HomogeneousNodeList.new(self.node, xpath_for(Param), Param) end # Set the parameter list. # Takes an array of Param objects. # definitions.params = [Param.new(), ...] def params=(parameters) self.params.replace(parameters) end protected def methods_to_test_for_equality [:coordinate_systems, :params] end end # A celestial coordinate system, to which the components of a position on the celestial sphere refer. class Coosys < VORuby::VOTable::Base ELEMENT_NAME = 'COOSYS' # Create a new coordinate system. # coosys = Coosys.new( # :id => 'J2000', # :equinox => 'J2000.', # :epoch => 'J2000.', # :system => 'eq_FK5' # ) # The default system is eq_FK5. def initialize(defn=nil) super(defn) self.system = 'eq_FK5' if !self.system end def id self.node['ID'] end def id=(i) @node['ID'] = i.to_s end def equinox self.node['equinox'] end def equinox=(e) @node['equinox'] = e.to_s end def epoch self.node['epoch'] end def epoch=(e) @node['epoch'] = e.to_s end def system self.node['system'] end # Set the system. # May be one of: eq_FK4, eq_FK5, ICRS, # ecl_FK4, ecl_FK5, galactic, supergalactic, xy, # barycentric or geo_app. def system=(s) @node['system'] = s.to_s end protected def methods_to_test_for_equality [:id, :equinox, :epoch, :system] end end # A global value associated with (usually) a table. class Param < VORuby::VOTable::Base include Castable ELEMENT_NAME = 'PARAM' def self.serialization_order [ :description, :values, :links, :id, :unit, :datatype, :arraysize, :precision, :width, :ref, :name, :ucd, :value ] end # Create a new parameter. # param = Param.new( # :name => 'Telescope', # :datatype => 'float', # :ucd => 'phys.size;instr.tel', # :unit => 'm', # :value => '3.6' # ) def initialize(defn=nil) super(defn) end def id self.node['ID'] end def id=(i) @node['ID'] = i.to_s end def unit self.node['unit'] end # Set the unit. # Units should be of the form outlined by Vizier[http://vizier.u-strasbg.fr/doc/catstd-3.2.htx]. def unit=(u) @node['unit'] = u.to_s end def datatype self.node['datatype'] end # Set the datatype. # Should be one of: boolean, bit, unsignedByte, short, int, long, # char, unicodeChar, float, double, floatComplex, doubleComplex. def datatype=(d) @node['datatype'] = d.to_s end def precision self.node['precision'] end # Set the precision of the value. # Should match [EF]?[1-9][0-9]* # param.precision = '1' def precision=(p) @node['precision'] = p.to_s end def width self.node['width'].to_i end def width=(w) @node['width'] = w.to_s end def ref self.node['ref'] end def ref=(r) @node['ref'] = r.to_s end def name self.node['name'] end def name=(n) @node['name'] = n.to_s end def ucd self.node['ucd'] end # Set the unified content descriptor or UCD[http://vizier.u-strasbg.fr/doc/UCD.htx]. def ucd=(u) @node['ucd'] = u.to_s end def arraysize self.node['arraysize'] end # Set the arraysize (if applicable). # i.e. *, 8x2 def arraysize=(a) @node['arraysize'] = a.to_s end # Retrieve the value of the parameter. # If _cast_ is false (default), the value is returned as a simple string. # However, if _cast_ is true, the datatype and arraysize are used to # determine a suitable object (or list of objects) to return. # See Castable#as_obj for more details. def value(cast=false) cast ? as_obj(self.value, self.datatype, self.arraysize) : self.node['value'] end # Set the value of the parameter. # An attempt is made to convert native ruby types using the datatype and # arraysize parameters. # param.value = Complex.new(1.1, 2.2) # etc. def value=(v) @node['value'] = v.is_a?(String) ? v : as_string(v, self.datatype, self.arraysize) end # Retrieve the description (Description). def description get_element(Description) end # Set the description (Description). def description=(d) set_element(Description, d) end # Retrieve the values (Values). def values get_element(Values) end # Set the values (Values). def values=(v) set_element(Values, v) end # Retrieve the links. # Returns a HomogeneousNodeList object. def links HomogeneousNodeList.new(self.node, xpath_for(Link), Link) end # Set the links. # Takes an array of Link objects. def links=(lnks) self.links.replace(lnks) end protected def methods_to_test_for_equality [ :id, :unit, :datatype, :precision, :width, :ref, :name, :ucd, :value, :arraysize, :description, :values, :links ] end end # A name-value pair. class Info < VORuby::VOTable::Base ELEMENT_NAME = 'INFO' # Create a new name-value pair. # info = Info.new(:name => 'history', :value => 'my first observation') def initialize(defn=nil) super(defn) end def id self.node['ID'] end def id=(i) @node['ID'] = i.to_s end def name self.node['name'] end def name=(n) @node['name'] = n.to_s end def value self.node['value'] end def value=(v) @node['value'] = v end protected def methods_to_test_for_equality [:id, :name, :value] end end # A description and the data values of some logically independent data structure # within the VOTable. class Resource < VORuby::VOTable::Base ELEMENT_NAME = 'RESOURCE' def self.serialization_order [ :description, :infos, :coordinate_systems, :params, :links, :tables, :resources, :id, :name, :type ] end # Create a new resource. # res = Resource.new( # :name => 'myFavouriteGalaxies', # :type => 'meta', # :params => [Param.new()], # :links => [Link.new()], # :tables => [Table.new()] # ) def initialize(defn=nil) super(defn) end def name self.node['name'] end def name=(n) @node['name'] = n.to_s end def id self.node['ID'] end def id=(i) @node['ID'] = i.to_s end # Retrieve the type. # A type of 'meta' means that the resource is descriptive only # (does not contain any actual data in any of its sub-elements). def type self.node['type'] end # Set the type. def type=(t) @node['type'] = t.to_s end # Retrieve the description (Description). def description get_element(Description) end # Set the description (Description). # res.description = Description.new(:text => 'A fascinating resource') def description=(d) set_element(Description, d) end # Retrieve the name-value pairs (Info). # Returns a HomogeneousNodeList. def infos HomogeneousNodeList.new(self.node, xpath_for(Info), Info) end # Set any name-value pairs. # res.infos = [Info.new(), ...] def infos=(is) self.infos.replace(is) end # Retrieve the coordinate systems (Coosys). # Returns a HomogeneousNodeList. def coordinate_systems HomogeneousNodeList.new(self.node, xpath_for(Coosys), Coosys) end # Set the coordinate systems (Coosys). # res.coordinate_systems = [Coosys.new(), ...] def coordinate_systems=(systems) self.coordinate_systems.replace(systems) end # Retrieve the parameters (Param). # Returns a HomogeneousNodeList. def params HomogeneousNodeList.new(self.node, xpath_for(Param), Param) end # Set the parameters (Param). # res.params = [Param.new(), ...] def params=(parameters) self.params.clear parameters.each do |p| self.params << p end end # Retrieve the links (Link). # Returns a HomogeneousNodeList. def links HomogeneousNodeList.new(self.node, xpath_for(Link), Link) end # Set the links (Link). # res.links = [Link.new(), ...] def links=(lnks) self.links.replace(lnks) end # Retrieve the tables (Table). # Returns a HomogeneousNodeList. def tables HomogeneousNodeList.new(self.node, xpath_for(Table), Table) end # Set the tables (Table). # res.tables = [Table.new(), ....] def tables=(tbls) self.tables.replace(tbls) end # Retrieve sub-resources (Resource). # Returns a HomogeneousNodeList. def resources HomogeneousNodeList.new(self.node, xpath_for(Resource), Resource) end # Set sub-resources. # res.resources = [Resource.new(), ...] def resources=(res) self.resources.replace(res) end # Convert a resource to HTML. def to_html builder = Builder::XmlMarkup.new(:indent => 2, :margin => 1) resource_opts = {:class => 'resource'} resource_opts[:id] = self.id if self.id builder.div(resource_opts){ |resource| resource.h3(self.name) if self.name resource.div(self.description.text, :class => 'description') if self.description self.tables.each do |tbl| resource << tbl.to_html end self.resources.each do |res| resource << res.to_html end } end protected def methods_to_test_for_equality [ :name, :id, :type, :description, :infos, :coordinate_systems, :params, :links, :tables, :resources ] end end # A link or pointer to other documents or data servers on the Internet through a URI. class Link < VORuby::VOTable::Base ELEMENT_NAME = 'LINK' # Create a new link. # link = Link.new( # :href => 'http://fits.gsfc.nasa.gov/nrao_data/samples/image/swp05569slg.fits', # :content_type => 'image/fits' # ) def initialize(defn=nil) super(defn) end def id self.node['ID'] end def id=(i) @node['ID'] = i.to_s end def content_role self.node['content-role'] end # Set the content role. # May be one of: query, hints, doc or location. def content_role=(c) @node['content-role'] = c.to_s end def content_type self.node['content-type'] end # Set the content type (i.e. mimetype). def content_type=(c) @node['content-type'] = c.to_s end def title self.node['title'] end def title=(t) @node['title'] = t.to_s end def value self.node['value'] end def value=(v) @node['value'] = v.to_s end # Retrieve the URL. # Returns a URI object. def href self.node['href'] ? URI.parse(self.node['href']) : nil end # Set the URL. # link.href = 'http://www.noao.edu/' # or... # link.href = URI.parse('http://www.noao.edu/') def href=(h) @node['href'] = h.to_s end def gref self.node['gref'] end def gref=(g) @node['gref'] = g.to_s end # Retrieve the action. # Returns a URI object. def action self.node['action'] ? URI.parse(self.node['action']) : nil end # Set the action. # link.action = 'http://www.retrieve-me.com/' # or... # link.action = URI.parse('http://www.retrieve-me.com/') def action=(a) @node['action'] = a.to_s end # Retrieve the link. # Understands any URL groked by open-uri (including file://). # Returns a File object. def retrieve shref = self.href if shref shref.scheme == 'file' ? File.open(shref.path) : open(shref.to_s) else nil end end protected def methods_to_test_for_equality [ :id, :content_role, :content_type, :title, :value, :href, :gref, :action ] end end # The basic structure of a votable. Essentially, tablular data + # a description of the columns of that data. class Table < VORuby::VOTable::Base ELEMENT_NAME= 'TABLE' def self.serialization_order [ :description, :fields, :links, :data, :id, :name, :ref ] end # Create a new table. # table = Table.new( # :name => 'results', # :fields => [Field.new()], # :data => Data.new(:format => TableData.new()) # ) def initialize(defn=nil) super(defn) end def id self.node['ID'] end def id=(i) @node['ID'] = i.to_s end def name self.node['name'] end def name=(n) @node['name'] = n.to_s end def ref self.node['ref'] end def ref=(r) @node['ref'] = r.to_s end # Retrieve the description (Description). def description get_element(Description) end # Set the description (Description). def description=(d) set_element(Description, d) end # Retrieve the data (Data). def data get_element(Data) end # Set the data (Data). def data=(d) set_element(Data, d) end # Retrieve the fields (Field), a.k.a. column descriptions. # Returns a HomogeneousNodeList. def fields HomogeneousNodeList.new(self.node, xpath_for(Field), Field) end # Set the fields. # table.fields = [Field.new(), ...] def fields=(flds) self.fields.replace(flds) end # Retrieve the links (Link). # Returns a HomogeneousNodeList. def links HomogeneousNodeList.new(self.node, xpath_for(Link), Link) end # Set the links (Link). # table.links = [Link.new(), ...] def links=(lnks) self.links.replace(lnks) end # Convert the table into HTML. # Not all aspects of the votable are translated. # table = Table.new( # :id => 'table1', # :description => Description.new(:text => 'A test table'), # :fields => [ # Field.new(:name => 'RA', :id => 'col1', :ucd => 'pos.eq.ra;meta.main', # :ref => 'J2000', :datatype => 'float', :width => 6, :precision => '2', :unit => 'deg'), # Field.new(:name => 'Dec', :id => 'col2', :ucd => 'pos.eq.dec;meta.main', # :ref => 'J2000', :datatype => 'float', :width => 6, :precision => '2', :unit => 'deg'), # Field.new(:name => 'Name', :id => 'col3', :ucd => 'meta.id;meta.main', # :datatype => 'char', :arraysize => '8*') # ], # :data => Data.new( # :format => TableData.new( # :trs => [ # Tr.new(:tds => [Td.new(:text => '123'), Td.new(:text => '456'), Td.new(:text => 'my_obj1')]), # Tr.new(:tds => [Td.new(:text => '789'), Td.new(:text => '112'), Td.new(:text => 'my_obj2')]), # Tr.new(:tds => [Td.new(:text => '145'), Td.new(:text => '178'), Td.new(:text => 'my_obj3')]) # ] # ) # ) # ) # # puts table.to_html # => # # # # # # # # # # # # # # # # # # # # # # # # # # #
A test table
#
#
RA
#
pos.eq.ra;meta.main
#
deg
# float #
#
#
#
Dec
#
pos.eq.dec;meta.main
#
deg
# float #
#
#
#
Name
#
meta.id;meta.main
# char # 8* #
#
123456my_obj1
789112my_obj2
145178my_obj3
2, :margin => 2) tbl_options = {} tbl_options[:id] = self.id if self.id builder.table(tbl_options) { |table| table.caption(self.description.text) if self.description table.thead { |thead| if self.fields.size > 0 thead.tr { |tr| self.fields.each do |field| tr.th { |th| th << field.to_html } end } end } if self.data and self.data.format table.tbody { |tbody| self.data.format.trs.each do |tr| tbody << tr.to_html end } end } end protected def methods_to_test_for_equality [ :id, :name, :ref, :description, :data, :fields, :links ] end end # The description of an actual table column. class Field < VORuby::VOTable::Base ELEMENT_NAME = 'FIELD' def self.serialization_order [ :description, :values, :links, :id, :unit, :datatype, :precision, :width, :ref, :name, :ucd, :arraysize, :type ] end # Create a new table column description. # field = Field.new( # :name => 'RA', # :id => 'col1', # :ucd => 'pos.eq.ra;meta.main', # :ref => 'J2000', # :datatype => 'float', # :width => 6, # :precision => '2', # :unit => 'deg' # ) def initialize(defn=nil) super(defn) end def id self.node['ID'] end def id=(i) @node['ID'] = i.to_s end def unit self.node['unit'] end # Set the unit. # Units should be of the form outlined by Vizier[http://vizier.u-strasbg.fr/doc/catstd-3.2.htx]. def unit=(u) @node['unit'] = u.to_s end def datatype self.node['datatype'] end # Set the datatype. # Should be one of: boolean, bit, unsignedByte, short, int, long, # char, unicodeChar, float, double, floatComplex, doubleComplex. def datatype=(d) @node['datatype'] = d.to_s end def precision self.node['precision'] end # Set the precision of the value. # Should match [EF]?[1-9][0-9]* # param.precision = '1' def precision=(p) @node['precision'] = p.to_s end def width self.node['width'] ? self.node['width'].to_i : nil end # Set the width. # Should be an integer. # param.width = 2 def width=(w) @node['width'] = w.to_s end def ref self.node['ref'] end def ref=(r) @node['ref'] = r.to_s end def name self.node['name'] end def name=(n) @node['name'] = n.to_s end def ucd self.node['ucd'] end # Set the unified content descriptor or UCD[http://vizier.u-strasbg.fr/doc/UCD.htx]. def ucd=(u) @node['ucd'] = u.to_s end def arraysize self.node['arraysize'] end # Set the arraysize (if applicable). # i.e. *, 8x2 def arraysize=(a) @node['arraysize'] = a.to_s end def type self.node['type'] end # Set the type. May be one of: hidden, no_query or trigger. def type=(t) @node['type'] = t.to_s end # Retrieve the description (Description). def description get_element(Description) end # Set the description (Description). def description=(d) set_element(Description, d) end # Retrieve the values (Values). def values HomogeneousNodeList.new(self.node, xpath_for(Values), Values) end # Set the values (Values). def values=(vals) self.values.replace(vals) end # Retrieve the links. # Returns a HomogeneousNodeList object. def links HomogeneousNodeList.new(self.node, xpath_for(Link), Link) end # Set the links. # Takes an array of Link objects. def links=(lnks) self.links.replace(lnks) end # Convert a field to HTML. def to_html builder = Builder::XmlMarkup.new(:indent => 2, :margin => 6) field_opts = {:class => 'field'} field_opts[:id] = self.id if self.id builder.div(field_opts) { |field| field.div(self.name, :class => 'name') field.div(self.ucd, :class => 'ucd') if self.ucd field.div(self.unit, :class => 'unit') if self.unit field.span(self.datatype, :class => 'datatype') if self.datatype field.span(self.arraysize, :class => 'arraysize') if self.arraysize } end protected def methods_to_test_for_equality [ :id, :unit, :datatype, :precision, :width, :ref, :name, :ucd, :arraysize, :type, :description, :values, :links ] end end # Holds subsidiary information about the domain of the data. class Values < VORuby::VOTable::Base ELEMENT_NAME = 'VALUES' def self.serialization_order [ :min, :max, :options, :id, :type, :null, :invalid ] end # Create some new subsidiary information about the domain. # values = Values.new( # :id => 'RAdomain', # :min => Min.new(:value => '0'), # :max => Max.new(:value => '360', :inclusive => false) # ) def initialize(defn=nil) super(defn) self.type = 'legal' if !self.type end def id self.node['ID'] end def id=(i) @node['ID'] = i.to_s end def type self.node['type'] end # Set the scope of values present. # Possible values are: legal, actual. # Defaults to legal. def type=(t) @node['type'] = t.to_s end def null self.node['null'] end # Set the value that is used to specify # non-existent data. def null=(n) @node['null'] = n.to_s end def invalid self.node['invalid'] end # Returns a boolean depending on whether the # value is considered invalid. def invalid? self.node['invalid'] == 'yes' end # Set the invalid flag. # Takes boolean value or the strings yes/no. def invalid=(i) if i.is_a?(TrueClass) @node['invalid'] = 'yes' elsif i.is_a?(FalseClass) @node['invalid'] = 'no' else @node['invalid'] = i.to_s end end # Retrieve the maximum of the value (Max). def max get_element(Max) end # Set the maximum of the value (Max). def max=(m) set_element(Max, m) end def min get_element(Min) end # Set the minimum of the value (Min). def min=(m) set_element(Min, m) end # Retrieve the possible values (Option) the column can take on. # Returns a HomogeneousNodeList object. def options HomogeneousNodeList.new(self.node, xpath_for(Option), Option) end # Set the possible values the column can take on. # values.options = [Option.new(), ...] def options=(opts) self.options.replace(opts) end protected def methods_to_test_for_equality [ :id, :type, :null, :invalid, :max, :min, :options ] end end # A minimum value a column can take on. class Min < VORuby::VOTable::Base ELEMENT_NAME = 'MIN' # Create a new minimum value for a column. # min = Min.new(:value => '0') def initialize(defn=nil) super(defn) self.inclusive = true if !self.inclusive end def value self.node['value'] end def value=(v) @node['value'] = v.to_s end def inclusive self.node['inclusive'] end # Check whether the minimum value is inclusive or not. # By default, the minimum is inclusive. def inclusive? self.node['inclusive'] == 'yes' end # Set whether the minimum value is inclusive or not. # Takes a boolean value or a yes/no string. # min.inclusive = false # or... # min.inclusive = 'no' def inclusive=(i) if i.is_a?(TrueClass) @node['inclusive'] = 'yes' elsif i.is_a?(FalseClass) @node['inclusive'] = 'no' else @node['inclusive'] = i.to_s end end protected def methods_to_test_for_equality [:value, :inclusive] end end # The maximum value a column may take on. class Max < VORuby::VOTable::Base ELEMENT_NAME = 'MAX' # Create a new maximum value for a column. # max = Max.new(:value => '360', :inclusive => false) def initialize(defn=nil) super(defn) self.inclusive = true if !self.inclusive end def value self.node['value'] end def value=(v) @node['value'] = v.to_s end def inclusive self.node['inclusive'] end # Check whether the maximum value is inclusive or not. # By default, the maximum is inclusive. def inclusive? self.node['inclusive'] == 'yes' end # Set whether the maximum value is inclusive or not. # Takes a boolean value or a yes/no string. # max.inclusive = false # or... # max.inclusive = 'no' def inclusive=(i) if i.is_a?(TrueClass) @node['inclusive'] = 'yes' elsif i.is_a?(FalseClass) @node['inclusive'] = 'no' else @node['inclusive'] = i.to_s end end protected def methods_to_test_for_equality [:value, :inclusive] end end # An enumeration of possible values. # Essentially a hierarchy of key-value pairs. class Option < VORuby::VOTable::Base ELEMENT_NAME = 'OPTION' # Create a new enumeration. # option = Option.new( # :name => 'author', # :value => 'David Gasson' # ) def initialize(defn=nil) super(defn) end def name self.node['name'] end def name=(n) @node['name'] = n.to_s end def value self.node['value'] end def value=(v) @node['value'] = v.to_s end protected def methods_to_test_for_equality [:name, :value] end end # The actual tabular data. class Data < VORuby::VOTable::Base ELEMENT_NAME = 'DATA' # Create a new body of data. # data = Data.new(:format => TableData.new()) def initialize(defn=nil) super(defn) end # Retrieve the formatted data. May be TableData, Binary or Fits. def format format_node = self.node.find_first("#{xpath_for(TableData)}|#{xpath_for(Binary)}|#{xpath_for(Fits)}") return nil if !format_node case format_node.name when 'TABLEDATA' then TableData.new(format_node) when 'BINARY' then Binary.new(format_node) when 'FITS' then Fits.new(format_node) else nil end end # Set the formatted data. Must be one of the allowed # data formats--TableData, Binary or Fits. def format=(f) raise 'Data format must be one of TableData, Binary or Fits' if ![TableData, Binary, Fits].include?(f.class) self.node.each_child do |c| c.remove! end set_element(f.class, f) end protected def methods_to_test_for_equality; [:format] end end # The most typical way of representing tabular data. class TableData < VORuby::VOTable::Base ELEMENT_NAME = 'TABLEDATA' # Create new table data. # tabledata = TableData.new(:trs => [Tr.new, Tr.new, Tr.new]) def initialize(defn=nil) super(defn) end # Retrieve the rows of data. # Returns a HomogeneousNodeList. def trs HomogeneousNodeList.new(self.node, xpath_for(Tr), Tr) end # Set the rows of data. # tabledata.rows = [Tr.new(), ...] def trs=(rows) self.trs.replace(rows) end protected def methods_to_test_for_equality; [:trs] end end # Tabular data represented as a binary format. class Binary < VORuby::VOTable::Base ELEMENT_NAME = 'BINARY' # Create a new binary table. # binary = Binary.new(:stream => Stream.new()) def initialize(defn=nil) super(defn) end # Retrieve the stream (Stream) representing the data. def stream get_element(Stream) end # Set the stream representing the data. # binary.stream = Stream.new() def stream=(s) set_element(Stream, s) end protected def methods_to_test_for_equality; [:stream] end end class Stream < VORuby::VOTable::Base ELEMENT_NAME = 'STREAM' # Represents a stream of bytes, either local or remote. def initialize(defn=nil) super(defn) self.type = 'locator' if !self.type self.actuate = 'onRequest' if !self.actuate self.encoding = 'none' if !self.encoding end def type self.node['type'] end # Set the type of stream. May be one of: locator or other. # Default is locator. def type=(t) @node['type'] = t.to_s end # Retrieve the URL of the stream. # Returns a URI object. def href self.node['href'] ? URI.parse(self.node['href']) : nil end # Set the URL of the stream. # link.href = 'http://www.noao.edu/my.dat' # or... # link.href = URI.parse('http://www.noao.edu/my.dat') def href=(h) @node['href'] = h.to_s end def actuate self.node['actuate'] end # Set when the stream should be retrieved. # Possible values are: onLoad, onRequest, other or none. # Default is onRequest. In this implementation the value of this # parameter doesn't effect when the stream is retrieved-- # you must always use #retrieve. def actuate=(a) @node['actuate'] = a.to_s end def encoding self.node['encoding'] end # Set the encoding of the stream (i.e. base64). # Default is none. def encoding=(e) @node['encoding'] = e.to_s end def expires self.node['expires'] ? DateTime.parse(self.node['expires']) : nil end # Retrieve when the data in the stream expires. # Returns a DateTime object. def expires=(e) @node['expires'] = e.to_s end def rights self.node['rights'] end # Set any authentication information necessary to access # the remote resource (i.e. a password to an encrypted document). # The current implementation ignores this value when retrieving # the stream. def rights=(r) @node['rights'] = r.to_s end # If the stream is text-based, it can be directly embedded # within the document, and can be retrieved with this method. def text self.node['content'] end # If the stream is text-based it can be directly embedded with # this method. # stream.text = 'AAAAAj/yVZiDGSSUwFZ6ypR4yGkADwAcQV0euAAIAAJBmMzNwZWZmkGle4tBR3jVQT9ocwAA' def text=(txt) @node['content'] = txt.to_s end # Retrieve the document specified by #href. # Returns a File object, of if the document is # directly embedded in the stream a StringIO object. def retrieve shref = self.href if shref shref.scheme == 'file' ? File.open(shref.path) : open(shref.to_s) else StringIO.new(self.text) end end protected def methods_to_test_for_equality [:type, :href, :actuate, :encoding, :expires, :rights, :text] end end # Tabular data represented as a FITS[http://archive.stsci.edu/fits/fits_standard/] file. class Fits < VORuby::VOTable::Base ELEMENT_NAME = 'FITS' # Create a new FITS data stream. # fits = Fits.new( # :extnum => 2, # :stream => Stream.new(:href => http://fits.gsfc.nasa.gov/nrao_data/samples/image/swp05569slg.fits') # ) def initialize(defn=nil) super(defn) end # Retrieve the FITS extension number. # Returns an integer. def extnum self.node['extnum'] ? self.node['extnum'].to_i : nil end # Set the FITS extension number. def extnum=(e) @node['extnum'] = e.to_s end # Retrieve the stream (Stream) associated with the FITS file. def stream get_element(Stream) end # Set the stream (Stream) associated with the FITS file. # fits.stream = Stream.new(:href => 'http://whatever/my_fits.fits') def stream=(s) set_element(Stream, s) end # Retrieves the FITS file locally and returns it. # If rfits[http://rubyforge.org/projects/rfits/] is present # a RFits::File object is returned (or a RFits::HDU subclass if #extnum is specified). # If rfits is _not_ present a File object is returned instead. def to_rfits io = self.stream.retrieve io_obj = if io.is_a?(StringIO) tmp = Tempfile.new('fits') tmp.write(io.read) tmp else io end # RFits isn't absolutely required, but it's nice to have. return io_obj if !VORuby.rfits? rfits = RFits::File.new(io_obj.path) self.extnum ? rfits[self.extnum - 1] : rfits end protected def methods_to_test_for_equality [:extnum, :stream] end end # A row of data in a TableData. class Tr < VORuby::VOTable::Base ELEMENT_NAME = 'TR' # Create a new row of data. # tr = Tr.new(:tds => [Td.new()]) def initialize(defn=nil) super(defn) end # Retrieve the table data elements in the row. # Returns a HomogeneousNodeList. def tds HomogeneousNodeList.new(self.node, xpath_for(Td), Td) end # Set the data elements of the row. # tr.tds = [Td.new(), ...] def tds=(columns) self.tds.replace(columns) end # Convert a table row to HTML. # tr = Tr.new(:tds => [Td.new(:text => 'hello'), Td.new(:text => 'world')]) # puts tr.to_html # => # # hello # world # def to_html builder = Builder::XmlMarkup.new(:indent => 2, :margin => 4) builder.tr { |tr| self.tds.each do |column| tr << column.to_html end } end protected def methods_to_test_for_equality; [:tds] end end # A data element in a table row. class Td < VORuby::VOTable::Base include Castable ELEMENT_NAME = 'TD' # Create a new data element. # td = Td.new(:text => '123.2') def initialize(defn=nil) super(defn) end def ref self.node['ref'] end def ref=(r) @node['ref'] = r.to_s end # Retrieve the value of the data element as text. def text self.node.content end # Set the value of the data element as text. def text=(txt) @node.content = txt.to_s end # Retrieve the value of the data element as an appropriate # object (or list of objects), according to the datatype and arraysize of the # Field corresponding to this column. See Castable#as_obj # for more details. def value cfield = self.field as_obj(self.text, cfield.datatype, cfield.arraysize) end # Set the value of the data element. # An attempt is made to convert native ruby types using the datatype and # arraysize parameters of the associated Field. def value=(v) cfield = self.field self.text = as_string(v, cfield.datatype, cfield.arraysize) end # Retrieve the Field associated with this column. def field node_path = self.node.path # use the xpath to determine the position pos = (node_path =~ /TD\[\d+\]$/) ? node_path.split('/').last.match(/TD\[(\d+)\]$/)[1] : '1' field_node = self.node.find_first("../../../../*[local-name()='FIELD'][#{pos}]") field_node ? Field.new(field_node) : raise("Unable to associate a FIELD with this TD") end # Convert a column to HTML. # td = Td.new(:text => 'hello') # puts td.to_html # => hello def to_html builder = Builder::XmlMarkup.new(:indent => 2, :margin => 5) builder.td(self.text) end protected def methods_to_test_for_equality; [:text, :ref] end end end end end