require 'voruby/votables/loader' # A set of classes designed to read and manipulate VOTables[http://www.ivoa.net/Documents/latest/VOT.html]. # In practice you are most likely interested in the TreeParsedVOTable. module VORuby module VOTables module VOTable # An implementation of a standard VOTable based on the VOTable XML-Schema # available at the US-VO[http://www.us-vo.org/VOTable/]. Typically one # wouldn't construct this object directly, but rather use something like # VOTable::TreeParsedVOTable to do so from an XML file. class VOTable attr_accessor :description, :definitions, :coosys, :params, :info, :resources, :id, :version # [_id_:] # The VOTable's ID attribute. # [_version_:] # The VOTable version. # [_description_:] # The VOTable DESCRIPTION element (type: VOTABLE::META::Description). # [_definitions_:] # The VOTable DEFINITIONS element (type: VOTABLE::META::Definitions). # [_coosys_:] # The coordinate system (COOSYS) definitions for the the VOTable # (type: VOTABLE::META::CooSys). # [_params_:] # The PARAM elements for the VOTable (type: VOTABLE::META::Param). # [_info_:] # The INFO elements for the VOTable (type: VOTABLE::META::Info). # [_resources_:] # The RESOURCE elements for the VOTable (type: VOTABLE::META::Resource). def initialize(id=nil, version='1.1', description=nil, definitions=nil, coosys=[], params=[], info=[], resources=[]) @id = id @version = version Misc::TypeCheck.new(description, Meta::Description).check() @description = description Misc::TypeCheck.new(definitions, Meta::Definitions).check() @definitions = definitions coosys.each{ |sys| Misc::TypeCheck.new(sys, Meta::CooSys).check() } @coosys = coosys params.each{ |param| Misc::TypeCheck.new(param, Meta::Param).check() } @params = params info.each{ |inf| Misc::TypeCheck.new(inf, Meta::Info).check() } @info = info resources.each{ |res| Misc::TypeCheck.new(res, Meta::Resource).check() } @resources = resources end # Get the compact string representation of the VOTable. def to_s coosys = @coosys.each{|x| x.to_s}.join('|') params = @params.each{|x| x.to_s}.join('|') info = @info.each{|x| x.to_s}.join('|') resources = @resources.each{|x| x.to_s}.join('|') return "{id=#{@id};version=#{@version};" + "description=#{@description};definitions=#{@definitions};" + "coosys=#{coosys};params=#{params};info=#{info};" + "resources=#{resources}}" end # A convenience method for accessing the fields of a votable, # rather than having to iterate through each resource and table # manually. # Returns a list of VOTABLE::META::Field objects. # [_res_:] # The resource from which to extract the table in question. # [_tbl_:] # The resource from which to extract the table in question. def fields(res=0, tbl=0) @resources[res].tables()[tbl].fields() if @resources[res] end # A convenience method for determining the number of columns in # a votable. # [_res_:] # The resource from which to extract the table in question. # [_tbl_:] # The resource from which to extract the table in question. def number_of_fields(res=0, tbl=0) return fields(res, tbl).length() || [] end # A convenience method for accessing the row data of a votable. # Returns a list of VOTABLE::DATA::TR objects. # [_res_:] # The resource from which to extract the table in question. # [_tbl_:] # The resource from which to extract the table in question. def rows(res=0, tbl=0) @resources[res].tables()[tbl].data().format().trs() end # A convenience method for determining the number of data rows # in a votable. # [_res_:] # The resource from which to extract the table in question. # [_tbl_:] # The resource from which to extract the table in question. def number_of_rows(res=0, tbl=0) return rows(res, tbl).length() end # Find the main positional right ascension and declination # fields in a votable using UCDs. This should work for both # version 1 and 1+ UCDs. # Returns a hash with the keys "ra" and "dec". # [_res_:] # The resource from which to extract the table in question. # [_tbl_:] # The resource from which to extract the table in question. def main_positional_fields(res=0, tbl=0) main_pos = {} field_count = 0 fields(res, tbl).each do |field| if field.ucd() != nil ucd = field.ucd() # Determine if the UCD is of intereset. if ucd.value() == 'POS_EQ_RA_MAIN' or ucd.value() == 'pos.eq.ra;meta.main' # RA main_pos["ra"] = [field_count, field] elsif ucd.value() == 'POS_EQ_DEC_MAIN' or ucd.value() == 'pos.eq.dec;meta.main' # Dec main_pos["dec"] = [field_count, field] end end field_count = field_count + 1 end return main_pos end # Find positional fields in a votable using UCDs. This should work # for both version 1 and 1+ UCDs. # Returns a hash of the positions of the fields and their # corresponding VOTABLE::META::Field object. # [_res_:] # The resource from which to extract the table in question. # [_tbl_:] # The resource from which to extract the table in question. def positional_fields(res=0, tbl=0) pos = {} field_count = 0 fields(res, tbl).each do |field| if field.ucd() != nil ucd = field.ucd() # Determine if the UCD is of interest if ucd.value() =~ /^POS.*/ or ucd.value() =~ /^pos\..*/ pos[field_count] = field end end field_count = field_count + 1 end return pos end # Find the date of observation fields in a votable using UCDs. # This should work for both version 1 and 1+ UCDs, including # experimental (e.g. VOX) ones. # Returns a hash of the positions of the fields and their # corresponding VOTABLE::META::Field object. # [_res_:] # The resource from which to extract the table in question. # [_tbl_:] # The resource from which to extract the table in question. def date_of_observation_fields(res=0, tbl=0) date_obs = {} field_count = 0 fields(res, tbl).each do |field| if field.ucd() != nil ucd = field.ucd() dt = field.datatype() # Determine if the UCD is of interest. if ucd.value() == 'VOX:Image_MJDateObs' or (dt.value() == 'double' and (ucd.value() == 'TIME_DATE' or ucd.value() == 'time.epoch')) date_obs[field_count] = field end end field_count = field_count + 1 end return date_obs end # Find time fields in a votable using UCDs. This should work # for both version 1 and 1+ UCDs, including experimental (e.g. VOX) ones. # Returns a hash of the positions of the fields and their # corresponding VOTABLE::META::Field object. # [_res_:] # The resource from which to extract the table in question. # [_tbl_:] # The resource from which to extract the table in question. def time_fields(res=0, tbl=0) times = {} field_count = 0 fields(res, tbl).each do |field| if field.ucd() != nil ucd = field.ucd() if ucd.value() =~ /^TIME.*/ or ucd.value() =~ /time\..*/ or ucd.value() == 'VOX:Image_MJDateObs' times[field_count] = field end end field_count = field_count + 1 end return times end # Find the column number(s) associated with a UCD. # Returns a list of column positions. # [_res_:] # The resource from which to extract the table in question. # [_tbl_:] # The resource from which to extract the table in question. def find_columns(ucd, res=0, tbl=0) columns = [] col_count = 0 fields = fields(res, tbl) if fields fields.each do |field| if field.ucd() != nil tbl_ucd = field.ucd() columns.push(col_count) if tbl_ucd.value() == ucd end col_count += 1 end end return columns end # Find the column number(s) associated with the # VOX:imageAccessReference UCD. # Returns a list of column positions. def image_access_reference_columns # The following is correct # find_columns('VOX:Image_AccessReference').first # The following is cause XMM ticks me off (not valid SIAP). if find_columns('VOX:Image_AccessReference').first != nil find_columns('VOX:Image_AccessReference').first else find_columns('DATA_LINK').first end end def image_ra_columns if find_columns('POS_EQ_RA_MAIN').first != nil find_columns('POS_EQ_RA_MAIN').first elsif find_columns('pos.eq.ra;meta.main').first != nil find_columns('pos.eq.ra;meta.main').first end end def image_dec_columns if find_columns('POS_EQ_DEC_MAIN').first != nil find_columns('POS_EQ_DEC_MAIN').first elsif find_columns('pos.eq.dec;meta.main').first != nil find_columns('pos.eq.dec;meta.main').first end end def image_filter_columns if find_columns('VOX:BandPass_ID').first != nil find_columns('VOX:BandPass_ID').first elsif find_columns('instr.filter').first != nil find_columns('instr.filter').first end end def image_date_obs_columns if find_columns('time.obs.start').first != nil find_columns('time.obs.start').first end end def image_telescope_columns if find_columns('instr.tel').first != nil find_columns('instr.tel').first end end def image_survey_columns if find_columns('VOX:Image_Title').first != nil find_columns('VOX:Image_Title').first elsif find_columns('ID_SURVEY').first != nil find_columns('ID_SURVEY').first end end def image_instrument_columns if find_columns('VOX:INST_ID').first != nil find_columns('VOX:INST_ID').first elsif find_columns('INST_ID').first != nil find_columns('INST_ID').first elsif find_columns('instr').first != nil find_columns('instr').first end end def image_sky_columns if find_columns('instr.skyLevel').first != nil find_columns('instr.skyLevel').first end end def image_zeropoint_columns if find_columns('arith.zp;phot').first != nil find_columns('arith.zp;phot').first end end def image_seeing_columns if find_columns('instr.obsty.seeing').first != nil find_columns('instr.obsty.seeing').first end end def image_depth_columns #if find_columns('').first != nil # find_columns('').first #end end def image_exptime_columns if find_columns('time.expo').first != nil find_columns('time.expo').first end end # Create headers for HTML table # [_res_:] # The resource from which to extract the table in question. # [_tbl_:] # The table inside the resource from which to extract the rows in question. # [_infer_add_to_cart_ref_:] # # [_add_to_cart_header_value_:] # # [_infer_access_ref_:] # Link the access reference URL associated with a row. # [_access_ref_header_value_:] # For the access reference column, place this value in the header. # [_access_ref_col_:] # A valid SIA VOTable will only ever have one VOX:Image_AccessReference. # [_header_class_:] # The class to assign the header of the HTML table. def create_headers(res, tbl, infer_add_to_cart_ref, add_to_cart_header_value, infer_access_ref, access_ref_header_value, access_ref_col, header_class, id=nil) headers = Array.new thead = "\n" thead << "\n" if infer_add_to_cart_ref thead << "#{add_to_cart_header_value}\n" headers.push("*") end if infer_access_ref thead << "#{access_ref_header_value}\n" headers.push(' ') end col_count = 0 fields(res, tbl).each do |field| if infer_access_ref and col_count == access_ref_col headers[1] = field.ucd.value else thead << "#{field.name}\n" headers.push(field.ucd.value) end col_count += 1 end thead << " \n" thead << "\n" headers.each do |h| thead << "#{h}\n" end thead << "\n" thead << "" return thead end def create_add_to_cart_link(link_ref, columns) access_ref_col = image_access_reference_columns() link_ref << '&resource=' + CGI.escape(columns[access_ref_col].value).to_s if access_ref_col ra_col = image_ra_columns() link_ref << '&rac=' + columns[ra_col].value.to_s if ra_col dec_col = image_dec_columns() link_ref << '&decc=' + columns[dec_col].value.to_s if dec_col filter_col = image_filter_columns() link_ref << '&filter=' + columns[filter_col].value.to_s if filter_col date_obs_col = image_date_obs_columns() link_ref << '&date_obs=' + columns[date_obs_col].value.to_s if date_obs_col teles_col = image_telescope_columns() link_ref << '&telescop=' + columns[teles_col].value.to_s if teles_col survey_col = image_survey_columns() link_ref << '&survey=' + columns[survey_col].value.to_s if survey_col instrum_col = image_instrument_columns() link_ref << '&instrument=' + columns[instrum_col].value.to_s if instrum_col sky_col = image_sky_columns() link_ref << '&sky=' + columns[sky_col].value.to_s if sky_col zerop_col = image_zeropoint_columns() link_ref << '&zeropoint=' + columns[zerop_col].value.to_s if zerop_col seeing_col = image_seeing_columns() link_ref << '&seeing=' + columns[seeing_col].value.to_s if seeing_col depth_col = image_depth_columns() link_ref << '&depth=' + columns[depth_col].value.to_s if depth_col exptime_col = image_exptime_columns() link_ref << '&exptime=' + columns[exptime_col].value.to_s if exptime_col return link_ref end # Create body for HTML table # [_res_:] # The resource from which to extract the table in question. # [_tbl_:] # The table inside the resource from which to extract the rows in question. # [_infer_add_to_cart_ref_:] # # [_add_to_cart_link_value_:] # # [_add_to_cart_link_ref_:] # # [_infer_access_ref_:] # Link the access reference URL associated with a row. # [_access_ref_link_value_:] # For the access reference column, link this word. # [_access_ref_col_:] # A valid SIA VOTable will only ever have one VOX:Image_AccessReference. # [_body_class_:] # The class to assign the body of the HTML table. # [_row_classes_:] # The class to assign the HTML table body rows. def create_body(res, tbl, infer_add_to_cart_ref, add_to_cart_link_value, add_to_cart_link_ref, infer_access_ref, access_ref_link_value, access_ref_col, body_class, row_classes) tbody = "\n" row_count = 0 rows(res, tbl).each do |tr| tbody << "\n" # Specially mark up the first column to link to the image. columns = tr.tds() if infer_add_to_cart_ref archive = add_to_cart_link_ref.slice(add_to_cart_link_ref.index('&archive='), add_to_cart_link_ref.length) archive = archive.slice('&archive='.length, archive.length) archive = archive.slice(0, archive.index('&')) if archive.index('&') link_id = 'add_' + archive.to_s + '_' + row_count.to_s url = create_add_to_cart_link(add_to_cart_link_ref, columns) js = "new Ajax.Request('#{url}', " + " {method: 'post', " + " onComplete: function(request){Element.hide('#{link_id}');}});" tbody << "#{add_to_cart_link_value}\n" end if infer_access_ref link_ref = columns[access_ref_col].value tbody << "#{access_ref_link_value}\n" end col_count = 0 columns.each do |td| tbody << "#{td.value}\n" if infer_access_ref and col_count != access_ref_col col_count += 1 end tbody << "\n" row_count += 1 end tbody << "" return tbody end # Create table for HTML VOTable # [_id_:] # The ID to assign to the HTML table as a whole. # [_show_border_:] # The boolean value to show HTML table border. # [_table_class_:] # The class to assign the HTML table as a whole. # [_thead_:] # The class to assign the HTML table thead. # [_tbody_:] # The class to assign the HTML table tbody. def create_table(id, show_border, table_class, thead, tbody) table = "\n" table << "#{thead}\n#{tbody}\n
" return table end # Convert the specified table in the specified resource into an HTML # table. # [_id_:] # The ID to assign to the HTML table as a whole. # [_add_to_cart_link_ref_:] # # [_res_:] # The resource from which to extract the table in question. # [_tbl_:] # The table inside the resource from which to extract the rows in question. # [_infer_add_to_cart_ref_:] # # [_add_to_cart_header_value_:] # # [_add_to_cart_link_value_:] # # [_infer_access_ref_:] # Link the access reference URL associated with a row. # [_access_ref_header_value_:] # For the access reference column, place this value in the header. # [_access_ref_link_value_:] # For the access reference column, link this word. # [_show_border_:] # The boolean value to show HTML table border # [_table_class_:] # The class to assign the HTML table as a whole. # [_header_class_:] # The class to assign the header of the HTML table. # [_body_class_:] # The class to assign the body of the HTML table. # [_row_classes_:] # The class to assign the HTML table body rows. def to_html(id=nil, add_to_cart_link_ref=nil, res=0, tbl=0, infer_add_to_cart_ref=true, add_to_cart_header_value='Add to Cart', add_to_cart_link_value='Add', infer_access_ref=true, access_ref_header_value='URL', access_ref_link_value='Retrieve', show_border=false, table_class='votable', header_class='header', body_class='body', row_classes=['row1', 'row2']) begin # A valid SIA VOTable will only ever have one VOX:Image_AccessReference. access_ref_col = image_access_reference_columns() if access_ref_col # Create headers thead = create_headers(res, tbl, infer_add_to_cart_ref, add_to_cart_header_value, infer_access_ref, access_ref_header_value, access_ref_col, header_class, id) # Create body tbody = create_body(res, tbl, infer_add_to_cart_ref, add_to_cart_link_value, add_to_cart_link_ref, infer_access_ref, access_ref_link_value, access_ref_col, body_class, row_classes) return create_table(id, show_border, table_class, thead, tbody) else title = 'No Data' message = 'VOTable not contains data.' return create_message_table(table_class, header_class, body_class, row_classes, title, message) end rescue Exception => e title = 'Error' message = e.backtrace#@resources[0].info[0].text().to_s() table = create_message_table(table_class, header_class, body_class, row_classes, title, message) end end def create_message_table(table_class, header_class, body_class, row_classes, title, message) table = "\n" table << " \n" table << " \n" table << " \n" table << " \n" table << " \n" table << " \n" table << " \n" table << " \n" table << " \n" table << " \n" table << "
#{title}
" return table end def self._find_attr_value(attributes, name) attributes.each do |qname, value| return value if qname.name == name end return nil end end end end end