voruby 1.1.1 → 2.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.
Files changed (257) hide show
  1. data/Rakefile.rb +107 -224
  2. data/lib/misc.rb +1 -0
  3. data/lib/misc/misc.rb +60 -0
  4. data/lib/misc/propertyfile.rb +31 -0
  5. data/lib/symphony.rb +1 -0
  6. data/lib/symphony/symphony.rb +247 -0
  7. data/lib/voruby.rb +186 -0
  8. data/lib/voruby/active_votable/active_votable.rb +468 -347
  9. data/lib/voruby/adql/1.0/adql.rb +2418 -0
  10. data/lib/voruby/adql/support.rb +2 -0
  11. data/lib/voruby/misc.rb +351 -0
  12. data/lib/voruby/misc/connection_monitor.rb +97 -0
  13. data/lib/voruby/misc/libxml_ext.rb +121 -0
  14. data/lib/voruby/misc/rexml_ext.rb +223 -0
  15. data/lib/voruby/resolver/resolver.rb +12 -0
  16. data/lib/voruby/resolver/sesame.rb +299 -0
  17. data/lib/voruby/sky_query/sky_query.rb +192 -0
  18. data/lib/voruby/stc/1.10/coords.rb +2272 -0
  19. data/lib/voruby/stc/1.10/region.rb +892 -0
  20. data/lib/voruby/stc/1.10/stc.rb +3271 -0
  21. data/lib/voruby/stc/1.30/stc.rb +8666 -0
  22. data/lib/voruby/stc/support.rb +2 -0
  23. data/lib/voruby/ucd/ucd.rb +173 -0
  24. data/lib/voruby/voevent/1.1/voevent.rb +1124 -0
  25. data/lib/voruby/voevent/support.rb +5 -0
  26. data/lib/voruby/votable/1.0/votable.rb +1807 -0
  27. data/lib/voruby/votable/1.1/votable.rb +2100 -0
  28. data/lib/voruby/votable/votable.rb +305 -0
  29. data/lib/voruby/wesix/wesix.rb +491 -0
  30. data/lib/voruby/xlink/1.2/xlink.rb +21 -0
  31. data/test/voruby/active_votable/complex.vot +60 -0
  32. data/test/voruby/active_votable/error.vot +6 -0
  33. data/test/voruby/active_votable/large.vot +130040 -0
  34. data/test/voruby/active_votable/simple1.vot +38 -0
  35. data/test/voruby/active_votable/simple2.vot +38 -0
  36. data/test/voruby/active_votable/test.rb +193 -0
  37. data/test/voruby/adql/1.0/adql-alias.sql +1 -0
  38. data/test/voruby/adql/1.0/adql-alias.xml +26 -0
  39. data/test/voruby/adql/1.0/adql-avg.sql +1 -0
  40. data/test/voruby/adql/1.0/adql-avg.xml +31 -0
  41. data/test/voruby/adql/1.0/adql-circle.sql +1 -0
  42. data/test/voruby/adql/1.0/adql-circle.xml +46 -0
  43. data/test/voruby/adql/1.0/adql-expr.sql +1 -0
  44. data/test/voruby/adql/1.0/adql-expr.xml +34 -0
  45. data/test/voruby/adql/1.0/adql-function.sql +1 -0
  46. data/test/voruby/adql/1.0/adql-function.xml +41 -0
  47. data/test/voruby/adql/1.0/adql-group.sql +1 -0
  48. data/test/voruby/adql/1.0/adql-group.xml +51 -0
  49. data/test/voruby/adql/1.0/adql-having.sql +1 -0
  50. data/test/voruby/adql/1.0/adql-having.xml +25 -0
  51. data/test/voruby/adql/1.0/adql-like.sql +1 -0
  52. data/test/voruby/adql/1.0/adql-like.xml +17 -0
  53. data/test/voruby/adql/1.0/adql-order.sql +1 -0
  54. data/test/voruby/adql/1.0/adql-order.xml +37 -0
  55. data/test/voruby/adql/1.0/adql-simple.sql +1 -0
  56. data/test/voruby/adql/1.0/adql-simple.xml +12 -0
  57. data/test/voruby/adql/1.0/adql-top.sql +1 -0
  58. data/test/voruby/adql/1.0/adql-top.xml +33 -0
  59. data/test/voruby/adql/1.0/test.rb +2220 -0
  60. data/test/voruby/misc/test.rb +32 -0
  61. data/test/voruby/resolver/sesame/test.rb +56 -0
  62. data/test/voruby/sky_query/test.rb +107 -0
  63. data/test/voruby/stc/1.10/coords_test.rb +3704 -0
  64. data/test/voruby/stc/1.10/region_test.rb +993 -0
  65. data/test/voruby/stc/1.10/stc-catalog-entry-location.xml +112 -0
  66. data/test/voruby/stc/1.10/stc-obs-data-location.xml +126 -0
  67. data/test/voruby/stc/1.10/stc-region-circle.xml +5 -0
  68. data/test/voruby/stc/1.10/stc-region-convex.xml +11 -0
  69. data/test/voruby/stc/1.10/stc-region-convexhull.xml +5 -0
  70. data/test/voruby/stc/1.10/stc-region-ellipse.xml +7 -0
  71. data/test/voruby/stc/1.10/stc-region-intersection.xml +25 -0
  72. data/test/voruby/stc/1.10/stc-region-negation.xml +7 -0
  73. data/test/voruby/stc/1.10/stc-region-polygon.xml +13 -0
  74. data/test/voruby/stc/1.10/stc-region-sector.xml +6 -0
  75. data/test/voruby/stc/1.10/stc-region-union.xml +25 -0
  76. data/test/voruby/stc/1.10/stc-resource-profile.xml +60 -0
  77. data/test/voruby/stc/1.10/stc-search-location.xml +54 -0
  78. data/test/voruby/stc/1.10/stc_test.rb +4626 -0
  79. data/test/voruby/stc/1.30/stc-catalog-entry-location.xml +210 -0
  80. data/test/voruby/stc/1.30/stc-obs-data-location-arecibo.xml +353 -0
  81. data/test/voruby/stc/1.30/stc-obs-data-location-fits.xml +250 -0
  82. data/test/voruby/stc/1.30/stc-obs-data-location-xlink.xml +63 -0
  83. data/test/voruby/stc/1.30/stc-obs-data-location.xml +216 -0
  84. data/test/voruby/stc/1.30/stc-resource-profile-unusual-ref-pos.xml +39 -0
  85. data/test/voruby/stc/1.30/stc-resource-profile.xml +129 -0
  86. data/test/voruby/stc/1.30/stc-search-location-arecibo.xml +86 -0
  87. data/test/voruby/stc/1.30/stc-search-location.xml +101 -0
  88. data/test/voruby/stc/1.30/test.rb +6274 -0
  89. data/test/voruby/ucd/test.rb +48 -0
  90. data/test/voruby/voevent/1.1/test.rb +812 -0
  91. data/test/{voevent/voevent_v1_1.xml → voruby/voevent/1.1/voevent.xml} +2 -2
  92. data/test/voruby/voregistry/0.3/test.rb +137 -0
  93. data/test/voruby/votable/1.0/test.rb +714 -0
  94. data/test/voruby/votable/1.0/votable.basic.xml +660 -0
  95. data/test/voruby/votable/1.0/votable.html +86 -0
  96. data/test/voruby/votable/1.0/votable.ns.xml +56 -0
  97. data/test/voruby/votable/1.1/test.rb +785 -0
  98. data/test/voruby/votable/1.1/votable.basic.xml +38 -0
  99. data/test/voruby/votable/1.1/votable.html +86 -0
  100. data/test/voruby/votable/1.1/votable.ns.xml +56 -0
  101. data/test/voruby/votable/test.rb +15 -0
  102. data/test/voruby/wesix/test.rb +268 -0
  103. data/test/voruby/wesix/testr.fits +28 -0
  104. metadata +234 -247
  105. data/REQUIREMENTS +0 -6
  106. data/lib/voruby/active_votable/loader.rb +0 -5
  107. data/lib/voruby/adql/adql.rb +0 -2787
  108. data/lib/voruby/adql/ext.rb +0 -14
  109. data/lib/voruby/adql/loader.rb +0 -6
  110. data/lib/voruby/adql/operations.rb +0 -54
  111. data/lib/voruby/adql/parser.rb +0 -160
  112. data/lib/voruby/adql/transforms.rb +0 -573
  113. data/lib/voruby/ext.rb +0 -17
  114. data/lib/voruby/loader.rb +0 -4
  115. data/lib/voruby/misc/propertyfile.rb +0 -36
  116. data/lib/voruby/plastic/applications.rb +0 -174
  117. data/lib/voruby/plastic/constants.rb +0 -30
  118. data/lib/voruby/plastic/loader.rb +0 -10
  119. data/lib/voruby/plastic/plastic.rb +0 -1
  120. data/lib/voruby/resources/conesearch/conesearch.rb +0 -9
  121. data/lib/voruby/resources/conesearch/conesearch_v0_2.rb +0 -55
  122. data/lib/voruby/resources/conesearch/conesearch_v0_3.rb +0 -50
  123. data/lib/voruby/resources/conesearch/conesearch_v1_0.rb +0 -72
  124. data/lib/voruby/resources/conesearch/loader.rb +0 -4
  125. data/lib/voruby/resources/loader.rb +0 -50
  126. data/lib/voruby/resources/nodes.rb +0 -190
  127. data/lib/voruby/resources/openskynode/loader.rb +0 -4
  128. data/lib/voruby/resources/openskynode/openskynode.rb +0 -9
  129. data/lib/voruby/resources/openskynode/openskynode_v0_1.rb +0 -54
  130. data/lib/voruby/resources/sia/loader.rb +0 -5
  131. data/lib/voruby/resources/sia/sia.rb +0 -9
  132. data/lib/voruby/resources/sia/sia_v0_6.rb +0 -90
  133. data/lib/voruby/resources/sia/sia_v0_7.rb +0 -89
  134. data/lib/voruby/resources/sia/sia_v1_0.rb +0 -122
  135. data/lib/voruby/resources/stsci.rb +0 -59
  136. data/lib/voruby/resources/vodataservice/coverage_v0_2.rb +0 -195
  137. data/lib/voruby/resources/vodataservice/coverage_v0_3.rb +0 -158
  138. data/lib/voruby/resources/vodataservice/loader.rb +0 -5
  139. data/lib/voruby/resources/vodataservice/vodataservice.rb +0 -9
  140. data/lib/voruby/resources/vodataservice/vodataservice_v0_4.rb +0 -189
  141. data/lib/voruby/resources/vodataservice/vodataservice_v0_5.rb +0 -163
  142. data/lib/voruby/resources/vodataservice/vodataservice_v1_0.rb +0 -221
  143. data/lib/voruby/resources/voregistry/loader.rb +0 -4
  144. data/lib/voruby/resources/voregistry/voregistry.rb +0 -9
  145. data/lib/voruby/resources/voregistry/voregistry_v0_2.rb +0 -40
  146. data/lib/voruby/resources/voregistry/voregistry_v0_3.rb +0 -30
  147. data/lib/voruby/resources/voregistry/voregistry_v1_0.rb +0 -86
  148. data/lib/voruby/resources/voresource/loader.rb +0 -17
  149. data/lib/voruby/resources/voresource/voresource.rb +0 -9
  150. data/lib/voruby/resources/voresource/voresource_v0_10.rb +0 -327
  151. data/lib/voruby/resources/voresource/voresource_v0_9.rb +0 -405
  152. data/lib/voruby/resources/voresource/voresource_v1_0.rb +0 -230
  153. data/lib/voruby/services/ext.rb +0 -11
  154. data/lib/voruby/services/gestalt/footprint.rb +0 -95
  155. data/lib/voruby/services/gestalt/wcs_fixer.rb +0 -105
  156. data/lib/voruby/services/gestalt/wesix.rb +0 -155
  157. data/lib/voruby/services/loader.rb +0 -7
  158. data/lib/voruby/services/registry/registry.rb +0 -53
  159. data/lib/voruby/services/resolver/resolver.rb +0 -35
  160. data/lib/voruby/services/schema/schema.rb +0 -644
  161. data/lib/voruby/sesame/loader.rb +0 -6
  162. data/lib/voruby/sesame/sesame_v1_0.rb +0 -64
  163. data/lib/voruby/simple/loader.rb +0 -6
  164. data/lib/voruby/simple/parameters.rb +0 -196
  165. data/lib/voruby/simple/sap.rb +0 -446
  166. data/lib/voruby/spacetime/loader.rb +0 -3
  167. data/lib/voruby/spacetime/spacetime.rb +0 -607
  168. data/lib/voruby/stc/coords_v1_20.rb +0 -900
  169. data/lib/voruby/stc/loader.rb +0 -55
  170. data/lib/voruby/stc/region_v1_20.rb +0 -274
  171. data/lib/voruby/stc/stc_v1_20.rb +0 -1196
  172. data/lib/voruby/util.rb +0 -27
  173. data/lib/voruby/voevent/loader.rb +0 -7
  174. data/lib/voruby/voevent/voevent_v1_0.rb +0 -213
  175. data/lib/voruby/voevent/voevent_v1_1.rb +0 -196
  176. data/lib/voruby/votables/chandra.rb +0 -373
  177. data/lib/voruby/votables/data.rb +0 -179
  178. data/lib/voruby/votables/galex.rb +0 -377
  179. data/lib/voruby/votables/int.rb +0 -354
  180. data/lib/voruby/votables/libxml_parser.rb +0 -411
  181. data/lib/voruby/votables/libxml_votable.rb +0 -67
  182. data/lib/voruby/votables/loader.rb +0 -10
  183. data/lib/voruby/votables/meta.rb +0 -763
  184. data/lib/voruby/votables/misc.rb +0 -51
  185. data/lib/voruby/votables/nsa.rb +0 -410
  186. data/lib/voruby/votables/rexml_parser.rb +0 -408
  187. data/lib/voruby/votables/rexml_votable.rb +0 -67
  188. data/lib/voruby/votables/sdss.rb +0 -356
  189. data/lib/voruby/votables/transforms.rb +0 -388
  190. data/lib/voruby/votables/tree.rb +0 -45
  191. data/lib/voruby/votables/types.rb +0 -391
  192. data/lib/voruby/votables/votable.rb +0 -687
  193. data/test/active_votable/database.yml +0 -6
  194. data/test/active_votable/test.vot +0 -168492
  195. data/test/active_votable/unittest.rb +0 -41
  196. data/test/adql/test1.adql +0 -49
  197. data/test/adql/test2.adql +0 -51
  198. data/test/adql/test3.adql +0 -81
  199. data/test/adql/test4.adql +0 -53
  200. data/test/adql/test5.adql +0 -55
  201. data/test/adql/test6.adql +0 -18
  202. data/test/adql/test7.adql +0 -48
  203. data/test/adql/unittest.rb +0 -1672
  204. data/test/plastic/test.rb +0 -44
  205. data/test/plastic/test.vot +0 -5385
  206. data/test/plastic/unittest.rb +0 -66
  207. data/test/resources/conesearch/conesearch_v0_3.xml +0 -31
  208. data/test/resources/conesearch/conesearch_v1_0.xml +0 -86
  209. data/test/resources/conesearch/unittest_v0_3.rb +0 -22
  210. data/test/resources/conesearch/unittest_v1_0.rb +0 -24
  211. data/test/resources/openskynode/open_sky_node_v0_1.xml +0 -32
  212. data/test/resources/openskynode/unittest_v0_1.rb +0 -31
  213. data/test/resources/sia/simple_image_access_v0_7.xml +0 -36
  214. data/test/resources/sia/simple_image_access_v1_0.xml +0 -122
  215. data/test/resources/sia/unittest_v0_7.rb +0 -24
  216. data/test/resources/sia/unittest_v1_0.rb +0 -29
  217. data/test/resources/stsci.xml +0 -336
  218. data/test/resources/unittest_stsci.rb +0 -25
  219. data/test/resources/vodataservice/catalog_service_resource_v1_0.xml +0 -128
  220. data/test/resources/vodataservice/data_collection_resource_v0_5.xml +0 -54
  221. data/test/resources/vodataservice/data_collection_resource_v1_0.xml +0 -117
  222. data/test/resources/vodataservice/data_service_resource_v1_0.xml +0 -115
  223. data/test/resources/vodataservice/sky_service_resource_v0_10.xml +0 -45
  224. data/test/resources/vodataservice/table_service_resource_v1_0.xml +0 -122
  225. data/test/resources/vodataservice/tabular_sky_service_resource_v0_10.xml +0 -60
  226. data/test/resources/vodataservice/unittest_v0_5.rb +0 -126
  227. data/test/resources/vodataservice/unittest_v1_0.rb +0 -151
  228. data/test/resources/voregistry/authority_resource_v0_3.xml +0 -20
  229. data/test/resources/voregistry/authority_resource_v1_0.xml +0 -82
  230. data/test/resources/voregistry/registry_service_v0_3.xml +0 -20
  231. data/test/resources/voregistry/registry_service_v1_0.xml +0 -107
  232. data/test/resources/voregistry/unittest_v0_3.rb +0 -31
  233. data/test/resources/voregistry/unittest_v1_0.rb +0 -34
  234. data/test/resources/voresource/organisation_resource_v1_0.xml +0 -90
  235. data/test/resources/voresource/resource_organisation_v0_10.xml +0 -22
  236. data/test/resources/voresource/resource_service_v0_10.xml +0 -19
  237. data/test/resources/voresource/resource_v0_10.xml +0 -19
  238. data/test/resources/voresource/resource_v1_0.xml +0 -79
  239. data/test/resources/voresource/service_resource_v1_0.xml +0 -91
  240. data/test/resources/voresource/unittest_v0_10.rb +0 -61
  241. data/test/resources/voresource/unittest_v0_9.rb +0 -4
  242. data/test/resources/voresource/unittest_v1_0.rb +0 -190
  243. data/test/services/gestalt/unittest.rb +0 -74
  244. data/test/services/registry/unittest.rb +0 -34
  245. data/test/services/resolver/unittest.rb +0 -38
  246. data/test/simple/unittest.rb +0 -46
  247. data/test/spacetime/unittest.rb +0 -39
  248. data/test/stc/catalog_entry_location_v1_20.xml +0 -112
  249. data/test/stc/obs_data_location_v1_20.xml +0 -108
  250. data/test/stc/search_location_v1_20.xml +0 -54
  251. data/test/stc/stc_resource_profile_v1_20.xml +0 -60
  252. data/test/stc/unittest_v1_20.rb +0 -620
  253. data/test/voevent/unittest_v1_0.rb +0 -79
  254. data/test/voevent/unittest_v1_1.rb +0 -70
  255. data/test/voevent/voevent_v1_0.xml +0 -96
  256. data/test/votables/test.vot +0 -366
  257. data/test/votables/unittest.rb +0 -54
@@ -0,0 +1,192 @@
1
+ require 'ostruct'
2
+
3
+ begin
4
+ require 'rubygems'
5
+ rescue LoadError; end
6
+
7
+ gem 'soap4r'
8
+ require 'soap/wsdlDriver'
9
+
10
+ require 'voruby/votable/1.1/votable'
11
+
12
+ module VORuby
13
+ module SkyQuery
14
+ class Service
15
+ attr_accessor :namespace
16
+ attr_reader :end_point
17
+
18
+ # Query the SkyQuery service. A VOTable::V1_1::VOTable is returned.
19
+ # Service.query('SELECT o.objid, o.ra, o.dec FROM SDSS:PhotoPrimary o WHERE o.type = 3')
20
+ # This is a convenience method for
21
+ # Service.new.query('SELECT o.objid, o.ra, o.dec FROM SDSS:PhotoPrimary o WHERE o.type = 3')
22
+ def self.query(adql, node=nil, options={})
23
+ self.new(options).query(adql, node)
24
+ end
25
+
26
+ # Create a new SkyQuery service instance.
27
+ # Currently the only valid keys in the <tt>options</tt> hash is <tt>:end_point</tt>
28
+ # representing the location of the SkyQuery soap service and <tt>:namespace</tt>
29
+ # representing the namespace of that service. The default is to use OpenSkyQuery
30
+ # (i.e. end_point = http://openskyquery.net/Sky/SkyPortal/SkyPortal.asmx and
31
+ # namespace = SkyPortal.ivoa.net)
32
+ # portal = SkyQuery.new
33
+ def initialize(options={})
34
+ self.namespace = options[:namespace] || 'SkyPortal.ivoa.net'
35
+ self.end_point = options[:end_point] || 'http://openskyquery.net/Sky/SkyPortal/SkyPortal.asmx'
36
+ end
37
+
38
+ # Set the locaton of the SkyPortal webservice.
39
+ def end_point=(end_point)
40
+ @end_point = end_point
41
+
42
+ # Create the driver.
43
+ # We don't use the WSDL since we won't expose all possible methods.
44
+ @portal = SOAP::RPC::Driver.new(self.end_point, self.namespace)
45
+ #@extractor.wiredump_dev = STDOUT
46
+ @portal.return_response_as_xml = true # in prep for dumping the result into a VORuby::VOTable
47
+
48
+ # Below, we're assuming the namespace prefix for SkyPortal.ivoa.net will always be n1.
49
+
50
+ # Queries
51
+ @portal.add_method_with_soapaction(
52
+ 'SubmitDistributedQuery',
53
+ "#{self.namespace}/SubmitDistributedQuery",
54
+ 'n1:qry', 'n1:outputType'
55
+ )
56
+ @portal.add_method_with_soapaction(
57
+ 'SubmitQuery',
58
+ "#{self.namespace}/SubmitQuery",
59
+ 'n1:qry', 'n1:node', 'n1:outputType'
60
+ )
61
+
62
+ # Table data
63
+ @portal.add_method_with_soapaction(
64
+ 'GetTables',
65
+ "#{self.namespace}/GetTables",
66
+ 'n1:node'
67
+ )
68
+ @portal.add_method_with_soapaction(
69
+ 'GetTableInfo',
70
+ "#{self.namespace}/GetTableInfo",
71
+ 'n1:node', 'n1:table'
72
+ )
73
+
74
+ # Column data
75
+ @portal.add_method_with_soapaction(
76
+ 'GetMetaColumns',
77
+ "#{self.namespace}/GetMetaColumns",
78
+ 'n1:node', 'n1:table'
79
+ )
80
+ @portal.add_method_with_soapaction(
81
+ 'GetColumnInfo',
82
+ "#{self.namespace}/GetColumnInfo",
83
+ 'n1:node', 'n1:table', 'n1:column'
84
+ )
85
+ @portal.add_method_with_soapaction(
86
+ 'GetColumns',
87
+ "#{self.namespace}/GetColumns",
88
+ 'n1:node', 'n1:table'
89
+ )
90
+
91
+ # Misc
92
+ @portal.add_method_with_soapaction(
93
+ 'GetAllSkyNodesVOTable',
94
+ "#{self.namespace}/GetAllSkyNodesVOTable"
95
+ )
96
+ end
97
+
98
+ # Find all nodes available from the SkyPortal service.
99
+ # Returns a VOTable::V1_1::VOTable.
100
+ def sky_nodes
101
+ response = @portal.call('GetAllSkyNodesVOTable')
102
+ parse_votable(response, 'GetAllSkyNodesVOTableResult')
103
+ end
104
+
105
+ # Query the SkyQuery service. A VOTable::V1_1::VOTable is returned.
106
+ # votable = portal.query('SELECT o.objid, o.ra, o.dec FROM SDSS:PhotoPrimary o WHERE o.type = 3') # typical, distributed query
107
+ # votable = portal.query('SELECT o.objid, o.ra, o.dec FROM SDSS:PhotoPrimary o WHERE o.type = 3', 'SDSS') # query a particular node (unusual)
108
+ def query(adql, node=nil)
109
+ response = node ?
110
+ @portal.call('SubmitQuery', adql.to_s, node, 'votable') :
111
+ @portal.call('SubmitDistributedQuery', adql.to_s, 'votable')
112
+
113
+ parse_votable(response)
114
+ end
115
+
116
+ # Retrieve the list of tables associated with the specified node.
117
+ # Returns a list of objects with #name, #description and #rows fields.
118
+ # tables = portal.tables('SDSS')
119
+ def tables(node)
120
+ response = @portal.call('GetTables', node.to_s)
121
+ XML::Parser.string(response).parse.root.find("//*[local-name()='MetaTable']").collect{ |mt| parse_meta_table(mt) }
122
+ end
123
+
124
+ # Retrieve the metadata associated with the specified table.
125
+ # Returns an object with #name, #description and #rows fields.
126
+ # info = portal.table_info('SDSS', 'PhotoPrimary')
127
+ def table_info(node, table)
128
+ response = @portal.call('GetTableInfo', node.to_s, table.to_s)
129
+ xml = XML::Parser.string(response).parse.root.find_first("//*[local-name()='GetTableInfoResult']")
130
+ parse_meta_table(xml)
131
+ end
132
+
133
+ # Retrieve the metadata associated with the columns of the specified table.
134
+ # Returns a list of objects with #name, #unit, #description and #ucd fields.
135
+ # columns = portal.meta_columns('SDSS', 'PhotoPrimary')
136
+ def meta_columns(node, table)
137
+ response = @portal.call('GetMetaColumns', node.to_s, table.to_s)
138
+ XML::Parser.string(response).parse.root.find("//*[local-name()='MetaColumn']").collect{ |mc| parse_meta_column(mc) }
139
+ end
140
+
141
+ # Retrieve the metadata associated with the specified column.
142
+ # Returns an object with #name, #unit, #description and #ucd fields.
143
+ # info = portal.column_info('SDSS', 'PhotoPrimary', 'modelMag_i')
144
+ def column_info(node, table, column)
145
+ response = @portal.call('GetColumnInfo', node.to_s, table.to_s, column.to_s)
146
+ xml = XML::Parser.string(response).parse.root.find_first("//*[local-name()='GetColumnInfoResult']")
147
+ parse_meta_column(xml)
148
+ end
149
+
150
+ # Retrieve the list of columns associated with the specified table.
151
+ # Returns a list of column names.
152
+ # column_names = portal.columns('SDSS', 'PhotoPrimary')
153
+ def columns(node, table)
154
+ response = @portal.call('GetColumns', node.to_s, table.to_s)
155
+ XML::Parser.string(response).parse.root.find("//*[local-name()='string']").collect { |c| c.content }
156
+ end
157
+
158
+ private
159
+
160
+ def parse_meta_table(xml)
161
+ return nil if !xml.child?
162
+
163
+ defn = {}
164
+ xml.each_child do |child|
165
+ defn[child.name.downcase.to_sym] = child.name == 'Rows' ? Integer(child.content) : child.content.strip
166
+ end
167
+
168
+ defn.empty? ? nil : OpenStruct.new(defn)
169
+ end
170
+
171
+ def parse_meta_column(xml)
172
+ return nil if !xml.child?
173
+
174
+ defn = {}
175
+ xml.each_child do |child|
176
+ defn[child.name.downcase.to_sym] = child.content.strip
177
+ end
178
+
179
+ defn.empty? ? nil : OpenStruct.new(defn)
180
+ end
181
+
182
+ def parse_votable(xml, element='VOTABLE')
183
+ root = XML::Parser.string(xml).parse.root
184
+ vot_node = root.find_first("//*[local-name()='#{element}']")
185
+ raise "Unable to find votable in response: #{root}" if !vot_node
186
+
187
+ VORuby::VOTable.from_xml(vot_node)
188
+ end
189
+ end
190
+
191
+ end
192
+ end
@@ -0,0 +1,2272 @@
1
+ require 'date'
2
+ require 'uri'
3
+ require 'voruby/stc/support'
4
+
5
+ module VORuby
6
+ module STC
7
+ module V1_10
8
+ module Coords
9
+ include XMLUtilities
10
+
11
+ # Basic unit type
12
+ class Unit < Enumeration; end
13
+
14
+ # Time units may be second (s), hour (h: 3600 s), day (d: 86400 s),
15
+ # Julian year (a, yr: 365.25 d), Julian century (cy: 36525 d), or
16
+ # empty (i.e., dimensionless) for ISO-8601 format
17
+ class TimeUnit < Unit
18
+ def self.choices
19
+ ['s', 'h', 'd', 'a', 'yr', 'cy', '']
20
+ end
21
+
22
+ def initialize(u='')
23
+ u ||= ''
24
+ super(u.to_s)
25
+ end
26
+ end
27
+
28
+ # Coordinate units may be angular (degrees, radians, hours, arcmins, arcsecs),
29
+ # linear (meters, km, mm, AUs, parsecs, kpc, Mpc, lightyears), or empty
30
+ # (i.e., dimensionless)
31
+ class PosUnit < Unit
32
+ def self.choices
33
+ [
34
+ 'deg', 'deg deg m', 'rad', 'h', 'arcmin', 'arcsec', 'm', 'km',
35
+ 'mm', 'au', 'pc', 'kpc', 'Mpc', 'lyr', ''
36
+ ]
37
+ end
38
+
39
+ def initialize(u='')
40
+ u ||= ''
41
+ super(u.to_s)
42
+ end
43
+ end
44
+
45
+ # Angular coordinate units may be degrees, radians, hours, arcmins, arcsecs
46
+ class AngleUnit < Unit
47
+ def self.choices
48
+ ['deg', 'rad', 'h', 'arcmin', 'arcsec']
49
+ end
50
+ end
51
+
52
+ # Velocity units are a posUnitType / velTimeType; the latter may be second,
53
+ # day, hour, year (a or yr), century, but not empty
54
+ class VelTimeUnit < Unit
55
+ def self.choices
56
+ ['s', 'h', 'd', 'a', 'yr', 'cy']
57
+ end
58
+ end
59
+
60
+ # Spectral units may be frequency (Hz, kHz, MHz, GHz), wavelength (meters,
61
+ # mm, micron, nm, Angstrom), or energy (eV, keV, MeV, GeV, TeV)
62
+ class SpectralUnit < Unit
63
+ def self.choices
64
+ [
65
+ 'Hz', 'kHz', 'MHz', 'GHz',
66
+ 'm', 'mm', 'micron', 'nm', 'A',
67
+ 'eV', 'keV', 'MeV', 'GeV', 'TeV'
68
+ ]
69
+ end
70
+ end
71
+
72
+ # The actual time scale used: TT, TAI, UTC, TDB, etc.
73
+ class TimeScale < Enumeration
74
+ def self.choices
75
+ [
76
+ 'TT', 'TDT', 'ET',
77
+ 'TDB', 'TCG', 'TCB',
78
+ 'TAI', 'IAT',
79
+ 'UTC', 'LST', 'LOCAL'
80
+ ]
81
+ end
82
+ end
83
+
84
+ # Substitution group for AbsoluteTime component of AstronTime
85
+ module AbsoluteTime
86
+ include SerializableToXml
87
+
88
+ def ==(time)
89
+ self.value == time.value
90
+ end
91
+ end
92
+
93
+ # ISO8601 time; note: only a limited subset of ISO 8601 is allowed: yyyy-mm-ddThh:mm:ss.sss...
94
+ class ISOTime
95
+ include AbsoluteTime
96
+
97
+ attr_reader :value
98
+
99
+ def initialize(dt)
100
+ self.value = dt
101
+ end
102
+
103
+ def value=(dt)
104
+ @value = dt.is_a?(DateTime) ? dt : DateTime.parse(dt.to_s)
105
+ end
106
+
107
+ def to_s
108
+ self.value.strftime("%FT%T.000")
109
+ end
110
+
111
+ def to_xml
112
+ el = element()
113
+ el.text = self.to_s
114
+ collapse_namespaces(el)
115
+ el
116
+ end
117
+
118
+ def self.from_xml(xml)
119
+ ISOTime.new(element_from(xml).text)
120
+ end
121
+ end
122
+
123
+ # IDREF to an ISO8601 time
124
+ class ISOTimeRef < IdRef
125
+ include AbsoluteTime
126
+ end
127
+
128
+ # JD time
129
+ class JDTime
130
+ include AbsoluteTime
131
+
132
+ attr_reader :value
133
+
134
+ def initialize(dt)
135
+ self.value = dt
136
+ end
137
+
138
+ def value=(dt)
139
+ @value = Float(dt)
140
+ end
141
+
142
+ def to_s
143
+ self.value.to_s
144
+ end
145
+
146
+ def to_datetime
147
+ DateTime.jd(self.value)
148
+ end
149
+
150
+ def to_xml
151
+ el = element()
152
+ el.text = self.value.to_s
153
+ collapse_namespaces(el)
154
+ el
155
+ end
156
+
157
+ def self.from_xml(xml)
158
+ JDTime.new(element_from(xml).text)
159
+ end
160
+ end
161
+
162
+ # IDREF to a JD time
163
+ class JDTimeRef < IdRef
164
+ include AbsoluteTime
165
+ end
166
+
167
+ # MJD time (=JD - 2400000.5)
168
+ class MJDTime
169
+ include AbsoluteTime
170
+
171
+ attr_reader :value
172
+
173
+ def initialize(dt)
174
+ self.value = dt
175
+ end
176
+
177
+ def value=(dt)
178
+ @value = Float(dt)
179
+ end
180
+
181
+ def to_s
182
+ self.value.to_s
183
+ end
184
+
185
+ def to_datetime
186
+ DateTime.jd(DateTime.mjd_to_jd(self.value))
187
+ end
188
+
189
+ def to_xml
190
+ el = element()
191
+ el.text = self.value.to_s
192
+ collapse_namespaces(el)
193
+ el
194
+ end
195
+
196
+ def self.from_xml(xml)
197
+ MJDTime.new(element_from(xml).text)
198
+ end
199
+ end
200
+
201
+ # IDREF to an MJD time
202
+ class MJDTimeRef < IdRef
203
+ include AbsoluteTime
204
+ end
205
+
206
+ # A relocatable time origin for simulations
207
+ class TimeOrigin < Enumeration
208
+ include AbsoluteTime
209
+
210
+ def self.choices; ['RELOCATABLE'] end
211
+
212
+ def initialize(to='RELOCATABLE')
213
+ self.value = to
214
+ end
215
+
216
+ def to_xml
217
+ el = element()
218
+ el.text = self.value.to_s
219
+ collapse_namespaces(el)
220
+ el
221
+ end
222
+
223
+ def self.from_xml(xml)
224
+ TimeOrigin.new(element_from(xml).text)
225
+ end
226
+ end
227
+
228
+ # Substitution group: Relative time is elapsed time relative to the absolute reference time component
229
+ module RelativeTime
230
+ include SerializableToXml
231
+
232
+ def ==(time)
233
+ self.offset == time.offset and self.unit == time.unit
234
+ end
235
+ end
236
+
237
+ # Actual elapsed time offset
238
+ class TimeOffset
239
+ include RelativeTime
240
+
241
+ attr_reader :offset, :unit
242
+
243
+ def initialize(offset, unit=TimeUnit.new('s'))
244
+ self.offset = offset
245
+ self.unit = unit
246
+ end
247
+
248
+ def offset=(offset)
249
+ @offset = Float(offset)
250
+ end
251
+
252
+ def unit=(u)
253
+ @unit = u.is_a?(TimeUnit) ? u : TimeUnit.new(u)
254
+ end
255
+
256
+ def to_s
257
+ "#{self.offset} #{self.unit}"
258
+ end
259
+
260
+ def to_xml
261
+ el = element()
262
+ el.text = self.offset.to_s
263
+ el.attributes["#{obj_ns.prefix}:unit"] = self.unit.to_s
264
+ collapse_namespaces(el)
265
+ el
266
+ end
267
+
268
+ def self.from_xml(xml)
269
+ node = element_from(xml)
270
+ TimeOffset.new(
271
+ Float(node.text),
272
+ node.attributes.get_attribute_ns(obj_ns.uri, 'unit').value
273
+ )
274
+ end
275
+ end
276
+
277
+ class TimeOffsetRef < IdRef
278
+ include RelativeTime
279
+
280
+ attr_reader :unit
281
+
282
+ def initialize(ref, unit=TimeUnit.new('s'))
283
+ super(ref)
284
+ self.unit = unit
285
+ end
286
+
287
+ def unit=(u)
288
+ @unit = u.is_a?(TimeUnit) ? u : TimeUnit.new(u)
289
+ end
290
+
291
+ def offset; self.value end
292
+ def offset=(offset); self.value = offset end
293
+
294
+ def to_s
295
+ "#{self.value} #{self.unit}"
296
+ end
297
+
298
+ def to_xml
299
+ el = element()
300
+ el.text = self.value.to_s
301
+ el.attributes["#{obj_ns.prefix}:unit"] = self.unit.to_s
302
+ collapse_namespaces(el)
303
+ el
304
+ end
305
+
306
+ def self.from_xml(xml)
307
+ node = element_from(xml)
308
+ TimeOffsetRef.new(
309
+ node.text,
310
+ node.attributes.get_attribute_ns(obj_ns.uri, 'unit').value
311
+ )
312
+ end
313
+ end
314
+
315
+ # AstronTime is the generalized astronomical time type and consists of two or three elements:
316
+ # TimeScale, optional relative time offset, and an absolute time (ISO8601 or a decimal JD or MJD;
317
+ # or it may be an IDREF to one of those three)
318
+ class AstronTime
319
+ include SerializableToXml
320
+
321
+ attr_reader :timescale, :relative_time, :absolute_time
322
+
323
+ def initialize(timescale, absolute_time, relative_time=nil)
324
+ self.timescale = timescale
325
+ self.absolute_time = absolute_time
326
+ self.relative_time = relative_time
327
+ end
328
+
329
+ def timescale=(ts)
330
+ raise_argument_required_error('timescale') if !ts
331
+ @timescale = ts.is_a?(TimeScale) ? ts : TimeScale.new(ts)
332
+ end
333
+
334
+ def absolute_time=(at)
335
+ raise TypeError, "invalid value '#{at}', expected AbsoluteTime" if !at.is_a?(AbsoluteTime)
336
+ @absolute_time = at
337
+ end
338
+
339
+ def relative_time=(rt)
340
+ raise TypeError, "invalid value '#{rt}', expected RelativeTime" if rt and !rt.is_a?(RelativeTime)
341
+ @relative_time = rt
342
+ end
343
+
344
+ def to_xml(name=nil, ns=nil)
345
+ el = element(name, ns)
346
+
347
+ timescale = REXML::Element.new("#{obj_ns.prefix}:Timescale")
348
+ timescale.text = self.timescale.to_s
349
+
350
+ el.add_element(timescale)
351
+ el.add_element(self.relative_time.to_xml) if self.relative_time
352
+ el.add_element(self.absolute_time.to_xml)
353
+
354
+ collapse_namespaces(el)
355
+
356
+ el
357
+ end
358
+
359
+ def self.from_xml(xml)
360
+ root = element_from(xml)
361
+
362
+ options = {
363
+ :timescale => TimeScale.new(REXML::XPath.first(root, 'x:Timescale', {'x' => obj_ns.uri}).text),
364
+ :absolute_time => xml_to_obj(root, AbsoluteTime, true)
365
+ }
366
+ options[:relative_time] = xml_to_obj(root, RelativeTime, true)
367
+
368
+ AstronTime.new(options[:timescale], options[:absolute_time], options[:relative_time])
369
+ end
370
+
371
+ def ==(time)
372
+ self.timescale == time.timescale and
373
+ self.absolute_time == time.absolute_time and
374
+ self.relative_time == time.relative_time
375
+ end
376
+ end
377
+
378
+ # For vector coordinates we need arrays of doubles
379
+ class DoubleArray < TypedArray
380
+ include SerializableToXml
381
+
382
+ def self.restricted_to; [Float] end
383
+
384
+ def self.from_xml(xml)
385
+ self.new(element_from(xml).text.split(' ').collect{ |n| Float(n) })
386
+ end
387
+
388
+ def to_xml(name=nil, ns=nil)
389
+ el = element(name, ns)
390
+ el.text = self.to_s
391
+ collapse_namespaces(el)
392
+ el
393
+ end
394
+ end
395
+
396
+ # A vector of 2 doubles
397
+ class Double2 < DoubleArray
398
+ def self.maximum_length; 2 end
399
+ def self.minimum_length; 2 end
400
+ end
401
+
402
+ # A vector of 3 doubles
403
+ class Double3 < DoubleArray
404
+ def self.maximum_length; 3 end
405
+ def self.minimum_length; 3 end
406
+ end
407
+
408
+ # A vector of 4 doubles
409
+ class Double4 < DoubleArray
410
+ def self.maximum_length; 4 end
411
+ def self.minimum_length; 4 end
412
+ end
413
+
414
+ # A vector of 9 doubles
415
+ class Double9 < DoubleArray
416
+ def self.maximum_length; 9 end
417
+ def self.minimum_length; 9 end
418
+ end
419
+
420
+ # The head element for the CoordinateValue substitution group
421
+ module CoordValue
422
+ include SerializableToXml
423
+ end
424
+
425
+ # Substitution group for a scalar coordinate value
426
+ module CValue
427
+ include CoordValue
428
+
429
+ def to_s
430
+ self.value.to_s
431
+ end
432
+
433
+ def ==(value)
434
+ self.value == value.value
435
+ end
436
+
437
+ def to_xml(name=nil, ns=nil)
438
+ el = element(name, ns)
439
+ el.text = self.value.to_s
440
+ collapse_namespaces(el)
441
+ el
442
+ end
443
+ end
444
+
445
+ # double
446
+ class Value
447
+ include CValue
448
+
449
+ attr_reader :value
450
+
451
+ def initialize(value)
452
+ self.value = value
453
+ end
454
+
455
+ def value=(v)
456
+ @value = Float(v)
457
+ end
458
+
459
+ def self.from_xml(xml)
460
+ Value.new(element_from(xml).text)
461
+ end
462
+ end
463
+
464
+ # IDREF to a Value
465
+ class ValueRef < IdRef
466
+ include CValue
467
+ end
468
+
469
+ # Substitution group for a scalar coordinate error
470
+ module CError
471
+ include SerializableToXml
472
+
473
+ def to_s
474
+ self.value.to_s
475
+ end
476
+
477
+ def ==(error)
478
+ self.value == error.value
479
+ end
480
+
481
+ def to_xml
482
+ el = element()
483
+ el.text = self.value.to_s
484
+ collapse_namespaces(el)
485
+ el
486
+ end
487
+ end
488
+
489
+ # double
490
+ class Error
491
+ include CError
492
+
493
+ attr_reader :value
494
+
495
+ def initialize(value)
496
+ self.value = value
497
+ end
498
+
499
+ def value=(v)
500
+ @value = Float(v)
501
+ end
502
+
503
+ def self.from_xml(xml)
504
+ Error.new(element_from(xml).text)
505
+ end
506
+ end
507
+
508
+ # IDREF to an Error
509
+ class ErrorRef < IdRef
510
+ include CError
511
+ end
512
+
513
+ # Substitution group for a scalar coordinate resolution
514
+ module CResolution
515
+ include SerializableToXml
516
+
517
+ def to_s
518
+ self.value.to_s
519
+ end
520
+
521
+ def ==(res)
522
+ self.value == res.value
523
+ end
524
+
525
+ def to_xml
526
+ el = element()
527
+ el.text = self.value.to_s
528
+ collapse_namespaces(el)
529
+ el
530
+ end
531
+ end
532
+
533
+ # double
534
+ class Resolution
535
+ include CResolution
536
+
537
+ attr_reader :value
538
+
539
+ def initialize(value)
540
+ self.value = value
541
+ end
542
+
543
+ def value=(v)
544
+ @value = Float(v)
545
+ end
546
+
547
+ def self.from_xml(xml)
548
+ Resolution.new(element_from(xml).text)
549
+ end
550
+ end
551
+
552
+ class ResolutionRef < IdRef
553
+ include CResolution
554
+ end
555
+
556
+ # Substitution group for a scalar coordinate size
557
+ module CSize
558
+ include SerializableToXml
559
+
560
+ def to_s
561
+ self.value.to_s
562
+ end
563
+
564
+ def ==(size)
565
+ self.value == size.value
566
+ end
567
+ end
568
+
569
+ # double
570
+ class Size
571
+ include CSize
572
+
573
+ attr_reader :value
574
+
575
+ def initialize(value)
576
+ self.value = value
577
+ end
578
+
579
+ def value=(v)
580
+ @value = Float(v)
581
+ end
582
+
583
+ def to_xml
584
+ el = element()
585
+ el.text = self.value.to_s
586
+ collapse_namespaces(el)
587
+ el
588
+ end
589
+
590
+ def self.from_xml(xml)
591
+ Size.new(element_from(xml).text)
592
+ end
593
+ end
594
+
595
+ # IDREF to a Size
596
+ class SizeRef < IdRef
597
+ include CSize
598
+ end
599
+
600
+ # Substitution group for a scalar coordinate pixel size
601
+ module CPixSize
602
+ include SerializableToXml
603
+
604
+ def to_s
605
+ self.value.to_s
606
+ end
607
+
608
+ def ==(pixsize)
609
+ self.value == pixsize.value
610
+ end
611
+ end
612
+
613
+ # double
614
+ class PixSize
615
+ include CPixSize
616
+
617
+ attr_reader :value
618
+
619
+ def initialize(value)
620
+ self.value = value
621
+ end
622
+
623
+ def value=(v)
624
+ @value = Float(v)
625
+ end
626
+
627
+ def to_xml
628
+ el = element()
629
+ el.text = self.value.to_s
630
+ collapse_namespaces(el)
631
+ el
632
+ end
633
+
634
+ def self.from_xml(xml)
635
+ PixSize.new(element_from(xml).text)
636
+ end
637
+ end
638
+
639
+ # IDREF to a PixelSize
640
+ class PixSizeRef < IdRef
641
+ include CPixSize
642
+ end
643
+
644
+ # Abstract coordinate type; a concrete Coordinate consists of a Value, Error,
645
+ # Resolution, Size, and PixSize
646
+ class CoordinateType
647
+ include SerializableToXml
648
+
649
+ attr_reader :name
650
+
651
+ def initialize(options)
652
+ if options.is_a?(Hash)
653
+ options.each { |key, value| send("#{key}=", value) }
654
+ else
655
+ self.name = options
656
+ end
657
+ end
658
+
659
+ def name=(n)
660
+ @name = n ? n.to_s : nil
661
+ end
662
+
663
+ def ==(c)
664
+ self.name == c.name
665
+ end
666
+ end
667
+
668
+ # Generic scalar coordinate type.
669
+ # Single CError, CResolution, CSize, CPixSize elements indicate
670
+ # definite values; pairs indicate ranges.
671
+ class ScalarCoordinateType < CoordinateType
672
+ attr_reader :value, :error, :resolution, :size, :pix_size
673
+
674
+ def initialize(options={})
675
+ options.each { |key, value| send("#{key}=", value) }
676
+ end
677
+
678
+ def value=(val)
679
+ raise_type_mismatch_error(val, CValue) if val
680
+ @value = val
681
+ end
682
+
683
+ def error=(err)
684
+ if err
685
+ err = [err] if !err.is_a?(Array)
686
+
687
+ if err.size > 0
688
+ raise ArgumentError, "#{err.size} > maximum length of 2" if err.size > 2
689
+ err.each { |e| raise_type_mismatch_error(e, CError) if e }
690
+ @error = err
691
+ else
692
+ @error = nil
693
+ end
694
+ end
695
+ end
696
+
697
+ def resolution=(res)
698
+ if res
699
+ res = [res] if !res.is_a?(Array)
700
+
701
+ if res.size > 0
702
+ raise ArgumentError, "#{res.size} > maximum length of 2" if res.size > 2
703
+ res.each { |r| raise_type_mismatch_error(r, CResolution) if r }
704
+ @resolution = res
705
+ else
706
+ @resolution = nil
707
+ end
708
+ end
709
+ end
710
+
711
+ def size=(sz)
712
+ if sz
713
+ sz = [sz] if !sz.is_a?(Array)
714
+
715
+ if sz.size > 0
716
+ raise ArgumentError, "#{sz.size} > maximum length of 2" if sz.size > 2
717
+ sz.each { |s| raise_type_mismatch_error(s, CSize) if s }
718
+ @size = sz
719
+ else
720
+ @size = nil
721
+ end
722
+ end
723
+ end
724
+
725
+ def pix_size=(psz)
726
+ if psz
727
+ psz = [psz] if !psz.is_a?(Array)
728
+
729
+ if psz.size > 0
730
+ raise ArgumentError, "#{psz.size} > maximum length of 2" if psz.size > 2
731
+ psz.each { |p| raise_type_mismatch_error(p, CPixSize) if p }
732
+ @pix_size = psz
733
+ else
734
+ @pix_size = nil
735
+ end
736
+ end
737
+
738
+ def to_xml(name=nil)
739
+ el = element(name)
740
+
741
+ name = REXML::Element.new("#{obj_ns.prefix}:Name")
742
+ name.text = self.name
743
+
744
+ el.add_element(name)
745
+ el.add_element(self.value.to_xml) if self.value
746
+ self.error.each { |e| el.add_element(e.to_xml) } if self.error
747
+ self.resolution.each { |r| el.add_element(r.to_xml) } if self.resolution
748
+ self.size.each { |s| el.add_element(s.to_xml) } if self.size
749
+ self.pix_size.each { |p| el.add_element(p.to_xml) } if self.pix_size
750
+
751
+ collapse_namespaces(el)
752
+
753
+ el
754
+ end
755
+ end
756
+
757
+ def ==(sc)
758
+ super(sc) and
759
+ self.value == sc.value and
760
+ self.error == sc.error and
761
+ self.resolution == sc.resolution and
762
+ self.size == sc.size and
763
+ self.pix_size == sc.pix_size
764
+ end
765
+ end
766
+
767
+ # Time coordinate type.
768
+ # Single CError, CResolution, CSize, CPixSize elements indicate definite values; pairs indicate ranges.
769
+ class TimeCoordinate < CoordinateType
770
+ include SerializableToXml
771
+
772
+ attr_reader :time_instant, :error, :resolution, :size, :pix_size
773
+
774
+ def initialize(options={})
775
+ options.each { |key, value| send("#{key}=", value) }
776
+ end
777
+
778
+ def time_instant=(ti)
779
+ raise_type_mismatch_error(ti, AstronTime) if ti
780
+ @time_instant = ti
781
+ end
782
+
783
+ def error=(err)
784
+ if err
785
+ err = [err] if !err.is_a?(Array)
786
+
787
+ if err.size > 0
788
+ raise ArgumentError, "#{err.size} > maximum length of 2" if err.size > 2
789
+ err.each { |e| raise_type_mismatch_error(e, CError) if e }
790
+ @error = err
791
+ else
792
+ @error = nil
793
+ end
794
+ end
795
+ end
796
+
797
+ def resolution=(res)
798
+ if res
799
+ res = [res] if !res.is_a?(Array)
800
+
801
+ if res.size > 0
802
+ raise ArgumentError, "#{res.size} > maximum length of 2" if res.size > 2
803
+ res.each { |r| raise_type_mismatch_error(r, CResolution) if r }
804
+ @resolution = res
805
+ else
806
+ @resolution = nil
807
+ end
808
+ end
809
+ end
810
+
811
+ def size=(sz)
812
+ if sz
813
+ sz = [sz] if !sz.is_a?(Array)
814
+
815
+ if sz.size > 0
816
+ raise ArgumentError, "#{sz.size} > maximum length of 2" if sz.size > 2
817
+ sz.each { |s| raise_type_mismatch_error(s, CSize) if s }
818
+ @size = sz
819
+ else
820
+ @size = nil
821
+ end
822
+ end
823
+ end
824
+
825
+ def pix_size=(psz)
826
+ if psz
827
+ psz = [psz] if !psz.is_a?(Array)
828
+
829
+ if psz.size > 0
830
+ raise ArgumentError, "#{psz.size} > maximum length of 2" if psz.size > 2
831
+ psz.each { |p| raise_type_mismatch_error(p, CPixSize) if p }
832
+ @pix_size = psz
833
+ else
834
+ @pix_size = nil
835
+ end
836
+ end
837
+ end
838
+
839
+ def unit=(u)
840
+ raise_type_mismatch_error(u, TimeUnit) if u
841
+ @unit = u
842
+ end
843
+
844
+ def unit
845
+ @unit || TimeUnit.new('s')
846
+ end
847
+
848
+ def to_xml(name=nil)
849
+ el = element(name)
850
+ el.attributes["#{obj_ns.prefix}:unit"] = self.unit.to_s if self.unit
851
+
852
+ name = REXML::Element.new("#{obj_ns.prefix}:Name")
853
+ name.text = self.name
854
+
855
+ el.add_element(name)
856
+ el.add_element(self.time_instant.to_xml('TimeInstant')) if self.time_instant
857
+ self.error.each { |e| el.add_element(e.to_xml) } if self.error
858
+ self.resolution.each { |r| el.add_element(r.to_xml) } if self.resolution
859
+ self.size.each { |s| el.add_element(s.to_xml) } if self.size
860
+ self.pix_size.each { |p| el.add_element(p.to_xml) } if self.pix_size
861
+
862
+ collapse_namespaces(el)
863
+
864
+ el
865
+ end
866
+
867
+ def self.from_xml(xml)
868
+ root = element_from(xml)
869
+
870
+ options = {
871
+ :name => REXML::XPath.first(root, 'x:Name', {'x' => obj_ns.uri}).text
872
+ }
873
+ unit = root.attributes.get_attribute_ns(obj_ns.uri, 'unit')
874
+ options[:unit] = TimeUnit.new(unit.value) if unit
875
+
876
+ time_instant = REXML::XPath.first(root, 'x:TimeInstant', {'x' => obj_ns.uri})
877
+ options[:time_instant] = AstronTime.from_xml(time_instant) if time_instant
878
+
879
+ options[:error] = xml_to_obj(root, CError)
880
+ options[:resolution] = xml_to_obj(root, CResolution)
881
+ options[:size] = xml_to_obj(root, CSize)
882
+ options[:pix_size] = xml_to_obj(root, CPixSize)
883
+
884
+ TimeCoordinate.new(options)
885
+ end
886
+
887
+ def ==(tc)
888
+ super(tc) and
889
+ self.time_instant == tc.time_instant and
890
+ self.error == tc.error and
891
+ self.resolution == tc.resolution and
892
+ self.size == tc.size and
893
+ self.pix_size == tc.pix_size and
894
+ self.unit == tc.unit
895
+ end
896
+ end
897
+
898
+ # 2 doubles with optional position angle
899
+ class Size2Type < Double2; end
900
+
901
+ # Substitution group for a 2-D coordinate value
902
+ module CValue2
903
+ include CoordValue
904
+ end
905
+
906
+ # double vector
907
+ class Value2 < Double2
908
+ include CValue2
909
+ end
910
+
911
+ # IDREF to a Value2
912
+ class Value2Ref < IdRef
913
+ include CValue2
914
+ end
915
+
916
+ # Substitution group for a 2-D coordinate error
917
+ module CError2; end
918
+
919
+ # size2Type Error element
920
+ class Error2 < Size2Type
921
+ include CError2
922
+ end
923
+
924
+ # 2x2 matrix Error element
925
+ class Error2Matrix < Double4
926
+ include CError2
927
+ end
928
+
929
+ # IDREF to an Error2
930
+ class Error2Ref < IdRef
931
+ include CError2
932
+ end
933
+
934
+ # Substitution group for a 2-D coordinate resolution
935
+ module CResolution2; end
936
+
937
+ # size2Type Resolution element
938
+ class Resolution2 < Size2Type
939
+ include CResolution2
940
+ end
941
+
942
+ # 2x2 matrix Resolution element
943
+ class Resolution2Matrix < Double4
944
+ include CResolution2
945
+ end
946
+
947
+ # IDREF to a Resolution2
948
+ class Resolution2Ref < IdRef
949
+ include CResolution2
950
+ end
951
+
952
+ # Substitution group for a 2-D coordinate size
953
+ module CSize2; end
954
+
955
+ # size2Type Size element
956
+ class Size2 < Size2Type
957
+ include CSize2
958
+ end
959
+
960
+ # 2x2 matrix Size element
961
+ class Size2Matrix < Double4
962
+ include CSize2
963
+ end
964
+
965
+ # IDREF to a Size2
966
+ class Size2Ref < IdRef
967
+ include CSize2
968
+ end
969
+
970
+ # Substitution group for a 2-D coordinate pixel size
971
+ module CPixSize2; end
972
+
973
+ # size2Type PixSize element
974
+ class PixSize2 < Size2Type
975
+ include CPixSize2
976
+ end
977
+
978
+ # 2x2 matrix PixSize element
979
+ class PixSize2Matrix < Double4
980
+ include CPixSize2
981
+ end
982
+
983
+ # IDREF to a PixSize2<
984
+ class PixSize2Ref < IdRef
985
+ include CPixSize2
986
+ end
987
+
988
+ # Generic 2-D coordinate type.
989
+ # Single CError2, CResolution2, CSize2, CPixSize2 elements indicate definite values; pairs indicate ranges.
990
+ class Vector2Coordinate < CoordinateType
991
+ attr_reader :value, :error, :resolution, :size, :pix_size
992
+
993
+ def initialize(options={})
994
+ options.each { |key, value| send("#{key}=", value) }
995
+ end
996
+
997
+ def value=(val)
998
+ raise_type_mismatch_error(val, CValue2) if val
999
+ @value = val
1000
+ end
1001
+
1002
+ def error=(err)
1003
+ err = [err] if err.class != Array
1004
+
1005
+ if err.size > 0
1006
+ raise ArgumentError, "#{err.size} > maximum length of 2" if err.size > 2
1007
+ err.each { |e| raise_type_mismatch_error(e, CError2) if e }
1008
+ @error = err
1009
+ else
1010
+ @error = nil
1011
+ end
1012
+ end
1013
+
1014
+ def resolution=(res)
1015
+ res = [res] if res.class != Array
1016
+
1017
+ if res.size > 0
1018
+ raise ArgumentError, "#{res.size} > maximum length of 2" if res.size > 2
1019
+ res.each { |r| raise_type_mismatch_error(r, CResolution2) if r }
1020
+ @resolution = res
1021
+ else
1022
+ @resolution = nil
1023
+ end
1024
+ end
1025
+
1026
+ def size=(sz)
1027
+ sz = [sz] if sz.class != Array
1028
+
1029
+ if sz.size > 0
1030
+ raise ArgumentError, "#{sz.size} > maximum length of 2" if sz.size > 2
1031
+ sz.each { |s| raise_type_mismatch_error(s, CSize2) if s }
1032
+ @size = sz
1033
+ else
1034
+ @size = nil
1035
+ end
1036
+ end
1037
+
1038
+ def pix_size=(psz)
1039
+ psz = [psz] if psz.class != Array
1040
+
1041
+ if psz.size > 0
1042
+ raise ArgumentError, "#{psz.size} > maximum length of 2" if psz.size > 2
1043
+ psz.each { |p| raise_type_mismatch_error(p, CPixSize2) if p }
1044
+ @pix_size = psz
1045
+ else
1046
+ @pix_size = nil
1047
+ end
1048
+ end
1049
+
1050
+ def ==(sc)
1051
+ super(sc) and
1052
+ self.value == sc.value and
1053
+ self.error == sc.error and
1054
+ self.resolution == sc.resolution and
1055
+ self.size == sc.size and
1056
+ self.pix_size == sc.pix_size
1057
+ end
1058
+ end
1059
+
1060
+ # 3 doubles with optional position angles
1061
+ class Size3Type < Double3; end
1062
+
1063
+ # Substitution group for a 3-D coordinate value
1064
+ module CValue3
1065
+ include CoordValue
1066
+ end
1067
+
1068
+ # Vector of 3 doubles
1069
+ class Value3 < Double3
1070
+ include CValue3
1071
+ end
1072
+
1073
+ # IDREF to a Value3
1074
+ class Value3Ref < IdRef
1075
+ include CValue3
1076
+ end
1077
+
1078
+ # Substitution group for a 3-D coordinate error
1079
+ module CError3; end
1080
+
1081
+ # Size3 Error element
1082
+ class Error3 < Size3Type
1083
+ include CError3
1084
+ end
1085
+
1086
+ # 3x3 matrix Error element
1087
+ class Error3Matrix < Double9
1088
+ include CError3
1089
+ end
1090
+
1091
+ # IDREF to an Error3
1092
+ class Error3Ref < IdRef
1093
+ include CError3
1094
+ end
1095
+
1096
+ # Substitution group for a 3-D coordinate resolution
1097
+ module CResolution3; end
1098
+
1099
+ # size3 Resolution element
1100
+ class Resolution3 < Size3Type
1101
+ include CResolution3
1102
+ end
1103
+
1104
+ # IDREF to a Resolution3
1105
+ class Resolution3Ref < IdRef
1106
+ include CResolution3
1107
+ end
1108
+
1109
+ # Substitution group for a 3-D coordinate size
1110
+ module CSize3; end
1111
+
1112
+ # size3 Size element
1113
+ class Size3 < Size3Type
1114
+ include CSize3
1115
+ end
1116
+
1117
+ # 3x3 matrix Size element
1118
+ class Size3Matrix < Double9
1119
+ include CSize3
1120
+ end
1121
+
1122
+ # IDREF to a Size3
1123
+ class Size3Ref < IdRef
1124
+ include CSize3
1125
+ end
1126
+
1127
+ # Substitution group for a 3-D coordinate pixel size
1128
+ module CPixSize3; end
1129
+
1130
+ # size3Type PixSize element
1131
+ class PixSize3 < Size3Type
1132
+ include CPixSize3
1133
+ end
1134
+
1135
+ # 3x3 matrix PixSize element
1136
+ class PixSize3Matrix < Double9
1137
+ include CPixSize3
1138
+ end
1139
+
1140
+ # IDREF to PixSize3
1141
+ class PixSize3Ref < IdRef
1142
+ include CPixSize3
1143
+ end
1144
+
1145
+ # Generic 3-D coordinate type.
1146
+ # Single CError3, CResolution3, CSize3, CPixSize3 elements indicate definite values; pairs indicate ranges
1147
+ class Vector3Coordinate < CoordinateType
1148
+ attr_reader :value, :error, :resolution, :size, :pix_size
1149
+
1150
+ def initialize(options={})
1151
+ options.each { |key, value| send("#{key}=", value) }
1152
+ end
1153
+
1154
+ def value=(val)
1155
+ raise_type_mismatch_error(val, CValue3) if val
1156
+ @value = val
1157
+ end
1158
+
1159
+ def error=(err)
1160
+ err = [err] if err.class != Array
1161
+
1162
+ if err.size > 0
1163
+ raise ArgumentError, "#{err.size} > maximum length of 2" if err.size > 2
1164
+ err.each { |e| raise_type_mismatch_error(e, CError3) if e }
1165
+ @error = err
1166
+ else
1167
+ @error = nil
1168
+ end
1169
+ end
1170
+
1171
+ def resolution=(res)
1172
+ res = [res] if res.class != Array
1173
+
1174
+ if res.size > 0
1175
+ raise ArgumentError, "#{res.size} > maximum length of 2" if res.size > 2
1176
+ res.each { |r| raise_type_mismatch_error(r, CResolution3) if r }
1177
+ @resolution = res
1178
+ else
1179
+ @resolution = nil
1180
+ end
1181
+ end
1182
+
1183
+ def size=(sz)
1184
+ sz = [sz] if sz.class != Array
1185
+
1186
+ if sz.size > 0
1187
+ raise ArgumentError, "#{sz.size} > maximum length of 2" if sz.size > 2
1188
+ sz.each { |s| raise_type_mismatch_error(s, CSize3) if s }
1189
+ @size = sz
1190
+ else
1191
+ @size = nil
1192
+ end
1193
+ end
1194
+
1195
+ def pix_size=(psz)
1196
+ psz = [psz] if psz.class != Array
1197
+
1198
+ if psz.size > 0
1199
+ raise ArgumentError, "#{psz.size} > maximum length of 2" if psz.size > 2
1200
+ psz.each { |p| raise_type_mismatch_error(p, CPixSize3) if p }
1201
+ @pix_size = psz
1202
+ else
1203
+ @pix_size = nil
1204
+ end
1205
+ end
1206
+
1207
+ def ==(sc)
1208
+ super(sc) and
1209
+ self.value == sc.value and
1210
+ self.error == sc.error and
1211
+ self.resolution == sc.resolution and
1212
+ self.size == sc.size and
1213
+ self.pix_size == sc.pix_size
1214
+ end
1215
+ end
1216
+
1217
+ # Coordinate substitution group
1218
+ module Coordinate
1219
+ include SerializableToXml
1220
+ end
1221
+
1222
+ # Generic scalar coordinate element
1223
+ class ScalarCoordinate < ScalarCoordinateType
1224
+ include Coordinate
1225
+
1226
+ attr_reader :unit
1227
+
1228
+ def unit=(u)
1229
+ @unit = u.to_s
1230
+ end
1231
+
1232
+ def ==(c)
1233
+ super(c) and self.unit == c.unit
1234
+ end
1235
+
1236
+ def to_xml
1237
+ el = super('ScalarCoordinate')
1238
+ el.attributes["#{obj_ns.prefix}:unit"] = self.unit.to_s if self.unit
1239
+ collapse_namespaces(el)
1240
+ el
1241
+ end
1242
+
1243
+ def self.from_xml(xml)
1244
+ root = element_from(xml)
1245
+
1246
+ options = {
1247
+ :name => REXML::XPath.first(root, 'x:Name', {'x' => obj_ns.uri}).text
1248
+ }
1249
+ unit = root.attributes.get_attribute_ns(obj_ns.uri, 'unit')
1250
+ options[:unit] = unit.value if unit
1251
+ options[:value] = xml_to_obj(root, CValue, true)
1252
+ options[:error] = xml_to_obj(root, CError)
1253
+ options[:resolution] = xml_to_obj(root, CResolution)
1254
+ options[:size] = xml_to_obj(root, CSize)
1255
+ options[:pix_size] = xml_to_obj(root, CPixSize)
1256
+
1257
+ ScalarCoordinate.new(options)
1258
+ end
1259
+
1260
+ def ==(c)
1261
+ super(c) and self.unit == c.unit
1262
+ end
1263
+ end
1264
+
1265
+ # Generic string coordinate element
1266
+ class StringCoordinate < CoordinateType
1267
+ include Coordinate
1268
+
1269
+ attr_reader :value, :unit
1270
+
1271
+ def value=(v)
1272
+ @value = v.to_s
1273
+ end
1274
+
1275
+ def unit=(u)
1276
+ @unit = u.to_s
1277
+ end
1278
+
1279
+ def ==(c)
1280
+ super(c) and
1281
+ self.value == c.value and
1282
+ self.unit == c.unit
1283
+ end
1284
+
1285
+ def to_xml
1286
+ el = element()
1287
+ el.attributes["#{obj_ns.prefix}:unit"] = self.unit.to_s if self.unit
1288
+
1289
+ name = REXML::Element.new("#{obj_ns.prefix}:Name")
1290
+ name.text = self.name
1291
+
1292
+ value = REXML::Element.new("#{obj_ns.prefix}:Value")
1293
+ value.text = self.value
1294
+
1295
+ el.add_element(name)
1296
+ el.add_element(value)
1297
+
1298
+ collapse_namespaces(el)
1299
+
1300
+ el
1301
+ end
1302
+
1303
+ def self.from_xml(xml)
1304
+ root = element_from(xml)
1305
+
1306
+ options = {
1307
+ :name => REXML::XPath.first(root, 'x:Name', {'x' => obj_ns.uri}).text,
1308
+ :value => REXML::XPath.first(root, 'x:Value', {'x' => obj_ns.uri}).text
1309
+ }
1310
+ unit = root.attributes.get_attribute_ns(obj_ns.uri, 'unit')
1311
+ options['unit'] = unit.value if unit
1312
+
1313
+ StringCoordinate.new(options)
1314
+ end
1315
+ end
1316
+
1317
+ # Generic coordinate element for pixels
1318
+ class PixelCoordinate < CoordinateType
1319
+ include Coordinate
1320
+
1321
+ attr_reader :value
1322
+
1323
+ def value=(v)
1324
+ @value = Float(v)
1325
+ end
1326
+
1327
+ def ==(c)
1328
+ super(c) and
1329
+ self.value == c.value
1330
+ end
1331
+
1332
+ def to_xml
1333
+ el = element()
1334
+
1335
+ if self.name
1336
+ name = REXML::Element.new("#{obj_ns.prefix}:Name")
1337
+ name.text = self.name
1338
+ el.add_element(name)
1339
+ end
1340
+
1341
+ value = REXML::Element.new("#{obj_ns.prefix}:Value")
1342
+ value.text = self.value.to_s
1343
+ el.add_element(value)
1344
+
1345
+ collapse_namespaces(el)
1346
+
1347
+ el
1348
+ end
1349
+
1350
+ def self.from_xml(xml)
1351
+ root = element_from(xml)
1352
+
1353
+ options = {
1354
+ :value => REXML::XPath.first(root, 'x:Value', {'x' => obj_ns.uri}).text
1355
+ }
1356
+
1357
+ name = REXML::XPath.first(root, 'x:Name', {'x' => obj_ns.uri})
1358
+ options[:name] = name.text if name
1359
+
1360
+ PixelCoordinate.new(options)
1361
+ end
1362
+ end
1363
+
1364
+ # Position coordinate substitution group
1365
+ module Position
1366
+ include SerializableToXml
1367
+ end
1368
+
1369
+ # 1-D Position coordinate
1370
+ class Position1D < ScalarCoordinateType
1371
+ include Position
1372
+
1373
+ attr_reader :unit
1374
+
1375
+ def initialize(options={})
1376
+ raise_argument_required_error('unit') if !options.has_key?(:unit)
1377
+ super(options)
1378
+ end
1379
+
1380
+ def unit=(u)
1381
+ raise_argument_required_error('unit') if !u
1382
+ raise_type_mismatch_error(u, PosUnit)
1383
+ @unit = u
1384
+ end
1385
+
1386
+ def to_xml
1387
+ el = element()
1388
+ el.attributes["#{obj_ns.prefix}:unit"] = self.unit.to_s if self.unit
1389
+
1390
+ name = REXML::Element.new("#{obj_ns.prefix}:Name")
1391
+ name.text = self.name
1392
+
1393
+ el.add_element(name)
1394
+ el.add_element(self.value.to_xml) if self.value
1395
+ self.error.each { |e| el.add_element(e.to_xml) } if self.error
1396
+ self.resolution.each { |r| el.add_element(r.to_xml) } if self.resolution
1397
+ self.size.each { |s| el.add_element(s.to_xml) } if self.size
1398
+ self.pix_size.each { |p| el.add_element(p.to_xml) } if self.pix_size
1399
+
1400
+ collapse_namespaces(el)
1401
+
1402
+ el
1403
+ end
1404
+
1405
+ def self.from_xml(xml)
1406
+ root = element_from(xml)
1407
+
1408
+ options = {
1409
+ :name => REXML::XPath.first(root, 'x:Name', {'x' => obj_ns.uri}).text
1410
+ }
1411
+ unit = root.attributes.get_attribute_ns(obj_ns.uri, 'unit')
1412
+ options[:unit] = PosUnit.new(unit.value) if unit
1413
+ options[:value] = xml_to_obj(root, CValue, true)
1414
+ options[:error] = xml_to_obj(root, CError)
1415
+ options[:resolution] = xml_to_obj(root, CResolution)
1416
+ options[:size] = xml_to_obj(root, CSize)
1417
+ options[:pix_size] = xml_to_obj(root, CPixSize)
1418
+
1419
+ Position1D.new(options)
1420
+ end
1421
+
1422
+ def ==(p)
1423
+ super(p) and
1424
+ self.unit == p.unit
1425
+ end
1426
+ end
1427
+
1428
+ # 2-D Position coordinate
1429
+ class Position2D < Vector2Coordinate
1430
+ include Position
1431
+
1432
+ attr_reader :unit
1433
+
1434
+ def initialize(options={})
1435
+ raise_argument_required_error('unit') if !options.has_key?(:unit)
1436
+ super(options)
1437
+ end
1438
+
1439
+ def unit=(u)
1440
+ raise_argument_required_error('unit') if !u
1441
+ raise_type_mismatch_error(u, PosUnit)
1442
+ @unit = u
1443
+ end
1444
+
1445
+ def to_xml
1446
+ el = element()
1447
+ el.attributes["#{obj_ns.prefix}:unit"] = self.unit.to_s if self.unit
1448
+
1449
+ name = REXML::Element.new("#{obj_ns.prefix}:Name")
1450
+ name.text = self.name
1451
+
1452
+ el.add_element(name)
1453
+ el.add_element(self.value.to_xml) if self.value
1454
+ self.error.each { |e| el.add_element(e.to_xml) } if self.error
1455
+ self.resolution.each { |r| el.add_element(r.to_xml) } if self.resolution
1456
+ self.size.each { |s| el.add_element(s.to_xml) } if self.size
1457
+ self.pix_size.each { |p| el.add_element(p.to_xml) } if self.pix_size
1458
+
1459
+ collapse_namespaces(el)
1460
+
1461
+ el
1462
+ end
1463
+
1464
+ def self.from_xml(xml)
1465
+ root = element_from(xml)
1466
+
1467
+ options = {
1468
+ :name => REXML::XPath.first(root, 'x:Name', {'x' => obj_ns.uri}).text
1469
+ }
1470
+ unit = root.attributes.get_attribute_ns(obj_ns.uri, 'unit')
1471
+ options[:unit] = PosUnit.new(unit.value) if unit
1472
+ options[:value] = xml_to_obj(root, CValue2, true)
1473
+ options[:error] = xml_to_obj(root, CError2)
1474
+ options[:resolution] = xml_to_obj(root, CResolution2)
1475
+ options[:size] = xml_to_obj(root, CSize2)
1476
+ options[:pix_size] = xml_to_obj(root, CPixSize2)
1477
+
1478
+ Position2D.new(options)
1479
+ end
1480
+
1481
+ def ==(p)
1482
+ super(p) and
1483
+ self.unit == p.unit
1484
+ end
1485
+ end
1486
+
1487
+ class Position3D < Vector3Coordinate
1488
+ include Position
1489
+
1490
+ attr_reader :unit
1491
+
1492
+ def initialize(options={})
1493
+ raise_argument_required_error('unit') if !options.has_key?(:unit)
1494
+ super(options)
1495
+ end
1496
+
1497
+ def unit=(u)
1498
+ raise_argument_required_error('unit') if !u
1499
+ raise_type_mismatch_error(u, PosUnit)
1500
+ @unit = u
1501
+ end
1502
+
1503
+ def to_xml
1504
+ el = element()
1505
+ el.attributes["#{obj_ns.prefix}:unit"] = self.unit.to_s if self.unit
1506
+
1507
+ name = REXML::Element.new("#{obj_ns.prefix}:Name")
1508
+ name.text = self.name
1509
+
1510
+ el.add_element(name)
1511
+ el.add_element(self.value.to_xml) if self.value
1512
+ self.error.each { |e| el.add_element(e.to_xml) } if self.error
1513
+ self.resolution.each { |r| el.add_element(r.to_xml) } if self.resolution
1514
+ self.size.each { |s| el.add_element(s.to_xml) } if self.size
1515
+ self.pix_size.each { |p| el.add_element(p.to_xml) } if self.pix_size
1516
+
1517
+ collapse_namespaces(el)
1518
+
1519
+ el
1520
+ end
1521
+
1522
+ def self.from_xml(xml)
1523
+ root = element_from(xml)
1524
+
1525
+ options = {
1526
+ :name => REXML::XPath.first(root, 'x:Name', {'x' => obj_ns.uri}).text
1527
+ }
1528
+ unit = root.attributes.get_attribute_ns(obj_ns.uri, 'unit')
1529
+ options[:unit] = PosUnit.new(unit.value) if unit
1530
+ options[:value] = xml_to_obj(root, CValue3, true)
1531
+ options[:error] = xml_to_obj(root, CError3)
1532
+ options[:resolution] = xml_to_obj(root, CResolution3)
1533
+ options[:size] = xml_to_obj(root, CSize3)
1534
+ options[:pix_size] = xml_to_obj(root, CPixSize3)
1535
+
1536
+ Position3D.new(options)
1537
+ end
1538
+
1539
+ def ==(p)
1540
+ super(p) and
1541
+ self.unit == p.unit
1542
+ end
1543
+ end
1544
+
1545
+ # Velocity coordinate substitution group
1546
+ module Velocity
1547
+ include SerializableToXml
1548
+
1549
+ attr_reader :unit, :vel_time_unit
1550
+
1551
+ def unit=(u)
1552
+ raise_type_mismatch_error(u, PosUnit)
1553
+ @unit = u
1554
+ end
1555
+
1556
+ def vel_time_unit=(vtu)
1557
+ raise_type_mismatch_error(vtu, VelTimeUnit)
1558
+ @vel_time_unit = vtu
1559
+ end
1560
+
1561
+ def ==(v)
1562
+ super(v) and
1563
+ self.unit == v.unit and
1564
+ self.vel_time_unit == v.vel_time_unit
1565
+ end
1566
+ end
1567
+
1568
+ # 1-D Velocity coordinate
1569
+ class Velocity1D < ScalarCoordinateType
1570
+ include Velocity
1571
+
1572
+ def initialize(options={})
1573
+ raise_argument_required_error('unit') if !options.has_key?(:unit)
1574
+ raise_argument_required_error('velocity time unit') if !options.has_key?(:vel_time_unit)
1575
+ super(options)
1576
+ end
1577
+
1578
+ def to_xml
1579
+ el = element()
1580
+ el.attributes["#{obj_ns.prefix}:unit"] = self.unit.to_s
1581
+ el.attributes["#{obj_ns.prefix}:vel_time_unit"] = self.vel_time_unit.to_s
1582
+
1583
+ name = REXML::Element.new("#{obj_ns.prefix}:Name")
1584
+ name.text = self.name
1585
+
1586
+ el.add_element(name)
1587
+ el.add_element(self.value.to_xml) if self.value
1588
+ self.error.each { |e| el.add_element(e.to_xml) } if self.error
1589
+ self.resolution.each { |r| el.add_element(r.to_xml) } if self.resolution
1590
+ self.size.each { |s| el.add_element(s.to_xml) } if self.size
1591
+ self.pix_size.each { |p| el.add_element(p.to_xml) } if self.pix_size
1592
+
1593
+ collapse_namespaces(el)
1594
+
1595
+ el
1596
+ end
1597
+
1598
+ def self.from_xml(xml)
1599
+ root = element_from(xml)
1600
+
1601
+ options = {
1602
+ :name => REXML::XPath.first(root, 'x:Name', {'x' => obj_ns.uri}).text
1603
+ }
1604
+ unit = root.attributes.get_attribute_ns(obj_ns.uri, 'unit')
1605
+ options[:unit] = PosUnit.new(unit.value)
1606
+ vel_time_unit = root.attributes.get_attribute_ns(obj_ns.uri, 'vel_time_unit')
1607
+ options[:vel_time_unit] = VelTimeUnit.new(vel_time_unit.value)
1608
+ options[:value] = xml_to_obj(root, CValue, true)
1609
+ options[:error] = xml_to_obj(root, CError)
1610
+ options[:resolution] = xml_to_obj(root, CResolution)
1611
+ options[:size] = xml_to_obj(root, CSize)
1612
+ options[:pix_size] = xml_to_obj(root, CPixSize)
1613
+
1614
+ Velocity1D.new(options)
1615
+ end
1616
+ end
1617
+
1618
+ # 2-D Velocity coordinate
1619
+ class Velocity2D < Vector2Coordinate
1620
+ include Velocity
1621
+
1622
+ def initialize(options={})
1623
+ raise_argument_required_error('unit') if !options.has_key?(:unit)
1624
+ raise_argument_required_error('velocity time unit') if !options.has_key?(:vel_time_unit)
1625
+ super(options)
1626
+ end
1627
+
1628
+ def to_xml
1629
+ el = element()
1630
+ el.attributes["#{obj_ns.prefix}:unit"] = self.unit.to_s
1631
+ el.attributes["#{obj_ns.prefix}:vel_time_unit"] = self.vel_time_unit.to_s
1632
+
1633
+ name = REXML::Element.new("#{obj_ns.prefix}:Name")
1634
+ name.text = self.name
1635
+
1636
+ el.add_element(name)
1637
+ el.add_element(self.value.to_xml) if self.value
1638
+ self.error.each { |e| el.add_element(e.to_xml) } if self.error
1639
+ self.resolution.each { |r| el.add_element(r.to_xml) } if self.resolution
1640
+ self.size.each { |s| el.add_element(s.to_xml) } if self.size
1641
+ self.pix_size.each { |p| el.add_element(p.to_xml) } if self.pix_size
1642
+
1643
+ collapse_namespaces(el)
1644
+
1645
+ el
1646
+ end
1647
+
1648
+ def self.from_xml(xml)
1649
+ root = element_from(xml)
1650
+
1651
+ options = {
1652
+ :name => REXML::XPath.first(root, 'x:Name', {'x' => obj_ns.uri}).text
1653
+ }
1654
+ unit = root.attributes.get_attribute_ns(obj_ns.uri, 'unit')
1655
+ options[:unit] = PosUnit.new(unit.value)
1656
+ vel_time_unit = root.attributes.get_attribute_ns(obj_ns.uri, 'vel_time_unit')
1657
+ options[:vel_time_unit] = VelTimeUnit.new(vel_time_unit.value)
1658
+ options[:value] = xml_to_obj(root, CValue2, true)
1659
+ options[:error] = xml_to_obj(root, CError2)
1660
+ options[:resolution] = xml_to_obj(root, CResolution2)
1661
+ options[:size] = xml_to_obj(root, CSize2)
1662
+ options[:pix_size] = xml_to_obj(root, CPixSize2)
1663
+
1664
+ Velocity2D.new(options)
1665
+ end
1666
+ end
1667
+
1668
+ # 3-D Velocity coordinate
1669
+ class Velocity3D < Vector3Coordinate
1670
+ include Velocity
1671
+
1672
+ def initialize(options={})
1673
+ raise_argument_required_error('unit') if !options.has_key?(:unit)
1674
+ raise_argument_required_error('velocity time unit') if !options.has_key?(:vel_time_unit)
1675
+ super(options)
1676
+ end
1677
+
1678
+ def to_xml
1679
+ el = element()
1680
+ el.attributes["#{obj_ns.prefix}:unit"] = self.unit.to_s
1681
+ el.attributes["#{obj_ns.prefix}:vel_time_unit"] = self.vel_time_unit.to_s
1682
+
1683
+ name = REXML::Element.new("#{obj_ns.prefix}:Name")
1684
+ name.text = self.name
1685
+
1686
+ el.add_element(name)
1687
+ el.add_element(self.value.to_xml) if self.value
1688
+ self.error.each { |e| el.add_element(e.to_xml) } if self.error
1689
+ self.resolution.each { |r| el.add_element(r.to_xml) } if self.resolution
1690
+ self.size.each { |s| el.add_element(s.to_xml) } if self.size
1691
+ self.pix_size.each { |p| el.add_element(p.to_xml) } if self.pix_size
1692
+
1693
+ collapse_namespaces(el)
1694
+
1695
+ el
1696
+ end
1697
+
1698
+ def self.from_xml(xml)
1699
+ root = element_from(xml)
1700
+
1701
+ options = {
1702
+ :name => REXML::XPath.first(root, 'x:Name', {'x' => obj_ns.uri}).text
1703
+ }
1704
+ unit = root.attributes.get_attribute_ns(obj_ns.uri, 'unit')
1705
+ options[:unit] = PosUnit.new(unit.value)
1706
+ vel_time_unit = root.attributes.get_attribute_ns(obj_ns.uri, 'vel_time_unit')
1707
+ options[:vel_time_unit] = VelTimeUnit.new(vel_time_unit.value)
1708
+ options[:value] = xml_to_obj(root, CValue3, true)
1709
+ options[:error] = xml_to_obj(root, CError3)
1710
+ options[:resolution] = xml_to_obj(root, CResolution3)
1711
+ options[:size] = xml_to_obj(root, CSize3)
1712
+ options[:pix_size] = xml_to_obj(root, CPixSize3)
1713
+
1714
+ Velocity3D.new(options)
1715
+ end
1716
+ end
1717
+
1718
+ # Specifies a FITS file and optionally a specific HDU by HDU number or HDU name
1719
+ class Fits
1720
+ include SerializableToXml
1721
+
1722
+ attr_reader :uri, :hdu_num, :hdu_name
1723
+
1724
+ def initialize(options={})
1725
+ raise_argument_required_error('URI') if !options.has_key?(:uri)
1726
+ options.each { |key, value| send("#{key}=", value) }
1727
+ end
1728
+
1729
+ def uri=(u)
1730
+ @uri = u.is_a?(URI) ? u : URI.parse(u.to_s)
1731
+ end
1732
+
1733
+ def hdu_num=(i)
1734
+ @hdu_num = Integer(i) if i
1735
+ end
1736
+
1737
+ def hdu_name=(n)
1738
+ @hdu_name = n.to_s if n
1739
+ end
1740
+
1741
+ def to_xml(name='FITSFile')
1742
+ el = element(name)
1743
+
1744
+ el.text = self.uri.to_s
1745
+ el.attributes["#{obj_ns.prefix}:hdu_num"] = self.hdu_num.to_s if self.hdu_num
1746
+ el.attributes["#{obj_ns.prefix}:hdu_name"] = self.hdu_name if self.hdu_name
1747
+
1748
+ collapse_namespaces(el)
1749
+
1750
+ el
1751
+ end
1752
+
1753
+ def self.from_xml(xml)
1754
+ root = element_from(xml)
1755
+
1756
+ options = {
1757
+ :uri => URI.parse(root.text)
1758
+ }
1759
+ hdu_num = root.attributes.get_attribute_ns(obj_ns.uri, 'hdu_num')
1760
+ options[:hdu_num] = hdu_num.value.to_i if hdu_num
1761
+ hdu_name = root.attributes.get_attribute_ns(obj_ns.uri, 'hdu_name')
1762
+ options[:hdu_name] = hdu_name.value if hdu_name
1763
+
1764
+ Fits.new(options)
1765
+ end
1766
+
1767
+ def ==(f)
1768
+ self.uri == f.uri and
1769
+ self.hdu_num == f.hdu_num and
1770
+ self.hdu_name == f.hdu_name
1771
+ end
1772
+ end
1773
+
1774
+ # Refers coordinate components to specific columns in the FITS file HDU
1775
+ class CoordFitsColumns
1776
+ include SerializableToXml
1777
+
1778
+ attr_reader :name, :value, :error, :resolution, :size, :pix_size
1779
+
1780
+ def initialize(options={})
1781
+ raise_argument_required_error('name') if !options.has_key?(:name)
1782
+ options.each { |key, value| send("#{key}=", value) }
1783
+ end
1784
+
1785
+ def name=(n)
1786
+ raise_argument_required_error('name') if !n
1787
+ @name = n.to_s
1788
+ end
1789
+
1790
+ # The column name for the coordinate value; comma-separated if multi-dimensional
1791
+ def value=(v)
1792
+ @value = v.to_s if v
1793
+ end
1794
+
1795
+ # The column name for the coordinate error; comma-separated if multi-dimensional
1796
+ def error=(e)
1797
+ @error = e.to_s if e
1798
+ end
1799
+
1800
+ # The column name for the coordinate resolution; comma-separated if multi-dimensional
1801
+ def resolution=(r)
1802
+ @resolution = r.to_s if r
1803
+ end
1804
+
1805
+ # The column name for the coordinate size; comma-separated if multi-dimensional
1806
+ def size=(s)
1807
+ @size = s.to_s if s
1808
+ end
1809
+
1810
+ # The column name for the coordinate pixel size; comma-separated if multi-dimensional
1811
+ def pix_size=(ps)
1812
+ @pix_size = ps.to_s if ps
1813
+ end
1814
+
1815
+ def to_xml(name)
1816
+ el = element(name)
1817
+
1818
+ name = REXML::Element.new("#{obj_ns.prefix}:Name")
1819
+ name.text = self.name
1820
+ el.add_element(name)
1821
+
1822
+ if self.value
1823
+ value = REXML::Element.new("#{obj_ns.prefix}:Value")
1824
+ value.text = self.value
1825
+ el.add_element(value)
1826
+ end
1827
+
1828
+ if self.error
1829
+ error = REXML::Element.new("#{obj_ns.prefix}:Error")
1830
+ error.text = self.error
1831
+ el.add_element(error)
1832
+ end
1833
+
1834
+ if self.resolution
1835
+ res = REXML::Element.new("#{obj_ns.prefix}:Resolution")
1836
+ res.text = self.resolution
1837
+ el.add_element(res)
1838
+ end
1839
+
1840
+ if self.size
1841
+ size = REXML::Element.new("#{obj_ns.prefix}:Size")
1842
+ size.text = self.size
1843
+ el.add_element(size)
1844
+ end
1845
+
1846
+ if self.pix_size
1847
+ pix_size = REXML::Element.new("#{obj_ns.prefix}:PixSize")
1848
+ pix_size.text = self.pix_size
1849
+ el.add_element(pix_size)
1850
+ end
1851
+
1852
+ collapse_namespaces(el)
1853
+
1854
+ el
1855
+ end
1856
+
1857
+ def self.from_xml(xml)
1858
+ root = element_from(xml)
1859
+
1860
+ options = {
1861
+ :name => REXML::XPath.first(root, 'x:Name', {'x' => obj_ns.uri}).text
1862
+ }
1863
+
1864
+ value = REXML::XPath.first(root, 'x:Value', {'x' => obj_ns.uri})
1865
+ options[:value] = value.text if value
1866
+
1867
+ error = REXML::XPath.first(root, 'x:Error', {'x' => obj_ns.uri})
1868
+ options[:error] = error.text if error
1869
+
1870
+ resolution = REXML::XPath.first(root, 'x:Resolution', {'x' => obj_ns.uri})
1871
+ options[:resolution] = resolution.text if resolution
1872
+
1873
+ size = REXML::XPath.first(root, 'x:Size', {'x' => obj_ns.uri})
1874
+ options[:size] = size.text if size
1875
+
1876
+ pix_size = REXML::XPath.first(root, 'x:PixSize', {'x' => obj_ns.uri})
1877
+ options[:pix_size] = pix_size.text if pix_size
1878
+
1879
+ CoordFitsColumns.new(options)
1880
+ end
1881
+
1882
+ def ==(c)
1883
+ self.name == c.name and
1884
+ self.value == c.value and
1885
+ self.error == c.error and
1886
+ self.resolution == c.resolution and
1887
+ self.size == c.size and
1888
+ self.pix_size == c.pix_size
1889
+ end
1890
+ end
1891
+
1892
+ # Coordinate references to a specific FITS file
1893
+ class AstroCoordsFile
1894
+ include SerializableToXml
1895
+
1896
+ attr_reader :file, :time, :position, :velocity, :spectral, :redshift
1897
+
1898
+ def initialize(options={})
1899
+ raise_argument_required_error('file') if !options.has_key?(:file)
1900
+ options.each { |key, value| send("#{key}=", value) }
1901
+ end
1902
+
1903
+ def file=(f)
1904
+ raise_argument_required_error('file') if !f
1905
+ raise_type_mismatch_error(f, Fits)
1906
+ @file = f
1907
+ end
1908
+
1909
+ def time=(t)
1910
+ raise_type_mismatch_error(t, CoordFitsColumns)
1911
+ @time = t
1912
+ end
1913
+
1914
+ def position=(p)
1915
+ raise_type_mismatch_error(p, CoordFitsColumns)
1916
+ @position = p
1917
+ end
1918
+
1919
+ def velocity=(v)
1920
+ raise_type_mismatch_error(v, CoordFitsColumns)
1921
+ @velocity = v
1922
+ end
1923
+
1924
+ def spectral=(s)
1925
+ raise_type_mismatch_error(s, CoordFitsColumns)
1926
+ @spectral = s
1927
+ end
1928
+
1929
+ def redshift=(r)
1930
+ raise_type_mismatch_error(r, CoordFitsColumns)
1931
+ @redshift = r
1932
+ end
1933
+
1934
+ def to_xml(name=nil)
1935
+ el = element(name)
1936
+
1937
+ el.add_element(self.file.to_xml('FITSFile'))
1938
+ el.add_element(self.time.to_xml('FITSTime')) if self.time
1939
+ el.add_element(self.position.to_xml('FITSPosition')) if self.position
1940
+ el.add_element(self.velocity.to_xml('FITSVelocity')) if self.velocity
1941
+ el.add_element(self.spectral.to_xml('FITSSpectral')) if self.spectral
1942
+ el.add_element(self.redshift.to_xml('FITSRedshift')) if self.redshift
1943
+
1944
+ collapse_namespaces(el)
1945
+
1946
+ el
1947
+ end
1948
+
1949
+ def self.from_xml(xml)
1950
+ root = element_from(xml)
1951
+
1952
+ options = {
1953
+ :file => Fits.from_xml(REXML::XPath.first(root, 'x:FITSFile', {'x' => obj_ns.uri})),
1954
+ }
1955
+
1956
+ time = REXML::XPath.first(root, 'x:FITSTime', {'x' => obj_ns.uri})
1957
+ options[:time] = CoordFitsColumns.from_xml(time) if time
1958
+
1959
+ position = REXML::XPath.first(root, 'x:FITSPosition', {'x' => obj_ns.uri})
1960
+ options[:position] = CoordFitsColumns.from_xml(position) if position
1961
+
1962
+ velocity = REXML::XPath.first(root, 'x:FITSVelocity', {'x' => obj_ns.uri})
1963
+ options[:velocity] = CoordFitsColumns.from_xml(velocity) if velocity
1964
+
1965
+ spectral = REXML::XPath.first(root, 'x:FITSSpectral', {'x' => obj_ns.uri})
1966
+ options[:spectral] = CoordFitsColumns.from_xml(spectral) if spectral
1967
+
1968
+ redshift = REXML::XPath.first(root, 'x:FITSRedshift', {'x' => obj_ns.uri})
1969
+ options[:redshift] = CoordFitsColumns.from_xml(redshift) if redshift
1970
+
1971
+ AstroCoordsFile.new(options)
1972
+ end
1973
+
1974
+ def ==(c)
1975
+ self.file == c.file and
1976
+ self.time == c.time and
1977
+ self.position == c.position and
1978
+ self.velocity == c.velocity and
1979
+ self.spectral == c.spectral and
1980
+ self.redshift == c.redshift
1981
+ end
1982
+ end
1983
+
1984
+ class ListOfCoordinates < TypedArray
1985
+ def self.restricted_to; [Coordinate] end
1986
+ end
1987
+
1988
+ class ListOfPixelCoordinates < TypedArray
1989
+ def self.restricted_to; [PixelCoordinate] end
1990
+ def self.minimum_length; 1 end
1991
+ end
1992
+
1993
+ # The generic coordsType
1994
+ class CoordsType
1995
+ attr_reader :coordinates, :coord_system_id
1996
+
1997
+ def initialize(options={})
1998
+ raise_argument_required_error('coord system id') if !options.has_key?(:coord_system_id)
1999
+ options.each { |key, value| send("#{key}=", value) }
2000
+ end
2001
+
2002
+ def coordinates=(cs)
2003
+ cs = ListOfCoordinates.new(cs) if cs.class == Array
2004
+ raise_type_mismatch_error(cs, ListOfCoordinates) if cs
2005
+ @coordinates = if cs
2006
+ cs.size > 0 ? cs : nil
2007
+ else
2008
+ nil
2009
+ end
2010
+ end
2011
+
2012
+ def coord_system_id=(sys)
2013
+ raise_argument_required_error('coord system id') if !sys
2014
+ if sys
2015
+ @coord_system_id = sys.is_a?(IdRef) ? sys : IdRef.new(sys.to_s)
2016
+ end
2017
+ end
2018
+
2019
+ def ==(c)
2020
+ self.coord_system_id == c.coord_system_id and
2021
+ self.coordinates == c.coordinates
2022
+ end
2023
+ end
2024
+
2025
+ class Spectral < ScalarCoordinateType
2026
+ include SerializableToXml
2027
+
2028
+ attr_reader :unit
2029
+
2030
+ def unit=(u)
2031
+ raise_type_mismatch_error(u, SpectralUnit) if u
2032
+ @unit = u
2033
+ end
2034
+
2035
+ def to_xml
2036
+ el = element()
2037
+ el.attributes["#{obj_ns.prefix}:unit"] = self.unit.to_s if self.unit
2038
+
2039
+ name = REXML::Element.new("#{obj_ns.prefix}:Name")
2040
+ name.text = self.name
2041
+
2042
+ el.add_element(name)
2043
+ el.add_element(self.value.to_xml) if self.value
2044
+ self.error.each { |e| el.add_element(e.to_xml) } if self.error
2045
+ self.resolution.each { |r| el.add_element(r.to_xml) } if self.resolution
2046
+ self.size.each { |s| el.add_element(s.to_xml) } if self.size
2047
+ self.pix_size.each { |p| el.add_element(p.to_xml) } if self.pix_size
2048
+
2049
+ collapse_namespaces(el)
2050
+
2051
+ el
2052
+ end
2053
+
2054
+ def self.from_xml(xml)
2055
+ root = element_from(xml)
2056
+
2057
+ options = {
2058
+ :name => REXML::XPath.first(root, 'x:Name', {'x' => obj_ns.uri}).text
2059
+ }
2060
+ unit = root.attributes.get_attribute_ns(obj_ns.uri, 'unit')
2061
+ options[:unit] = SpectralUnit.new(unit.value) if unit
2062
+ options[:value] = xml_to_obj(root, CValue, true)
2063
+ options[:error] = xml_to_obj(root, CError)
2064
+ options[:resolution] = xml_to_obj(root, CResolution)
2065
+ options[:size] = xml_to_obj(root, CSize)
2066
+ options[:pix_size] = xml_to_obj(root, CPixSize)
2067
+
2068
+ Spectral.new(options)
2069
+ end
2070
+
2071
+ def ==(c)
2072
+ super(c) and self.unit == c.unit
2073
+ end
2074
+ end
2075
+
2076
+ class Redshift < ScalarCoordinateType
2077
+ include SerializableToXml
2078
+
2079
+ attr_reader :unit, :vel_time_unit
2080
+
2081
+ def unit=(u)
2082
+ raise_type_mismatch_error(u, PosUnit) if u
2083
+ @unit = u
2084
+ end
2085
+
2086
+ def vel_time_unit=(vtu)
2087
+ raise_type_mismatch_error(vtu, VelTimeUnit) if vtu
2088
+ @vel_time_unit = vtu
2089
+ end
2090
+
2091
+ def to_xml
2092
+ el = element()
2093
+ el.attributes["#{obj_ns.prefix}:unit"] = self.unit.to_s if self.unit
2094
+ el.attributes["#{obj_ns.prefix}:vel_time_unit"] = self.vel_time_unit.to_s if self.vel_time_unit
2095
+
2096
+ name = REXML::Element.new("#{obj_ns.prefix}:Name")
2097
+ name.text = self.name
2098
+
2099
+ el.add_element(name)
2100
+ el.add_element(self.value.to_xml) if self.value
2101
+ self.error.each { |e| el.add_element(e.to_xml) } if self.error
2102
+ self.resolution.each { |r| el.add_element(r.to_xml) } if self.resolution
2103
+ self.size.each { |s| el.add_element(s.to_xml) } if self.size
2104
+ self.pix_size.each { |p| el.add_element(p.to_xml) } if self.pix_size
2105
+
2106
+ collapse_namespaces(el)
2107
+
2108
+ el
2109
+ end
2110
+
2111
+ def self.from_xml(xml)
2112
+ root = element_from(xml)
2113
+
2114
+ options = {
2115
+ :name => REXML::XPath.first(root, 'x:Name', {'x' => obj_ns.uri}).text
2116
+ }
2117
+ unit = root.attributes.get_attribute_ns(obj_ns.uri, 'unit')
2118
+ options[:unit] = PosUnit.new(unit.value) if unit
2119
+ vel_time_unit = root.attributes.get_attribute_ns(obj_ns.uri, 'vel_time_unit')
2120
+ options[:vel_time_unit] = VelTimeUnit.new(vel_time_unit.value) if vel_time_unit
2121
+ options[:value] = xml_to_obj(root, CValue, true)
2122
+ options[:error] = xml_to_obj(root, CError)
2123
+ options[:resolution] = xml_to_obj(root, CResolution)
2124
+ options[:size] = xml_to_obj(root, CSize)
2125
+ options[:pix_size] = xml_to_obj(root, CPixSize)
2126
+
2127
+ Redshift.new(options)
2128
+ end
2129
+
2130
+ def ==(c)
2131
+ super(c) and
2132
+ self.unit == c.unit and
2133
+ self.vel_time_unit == c.vel_time_unit
2134
+ end
2135
+ end
2136
+
2137
+ # The astronomical (STC) coordsType
2138
+ class AstroCoordsType < CoordsType
2139
+ attr_reader :time, :position, :velocity, :spectral, :redshift, :coord_file
2140
+
2141
+ def time=(t)
2142
+ raise_type_mismatch_error(t, TimeCoordinate) if t
2143
+ @time = t
2144
+ end
2145
+
2146
+ def position=(p)
2147
+ raise_type_mismatch_error(p, Position) if p
2148
+ @position = p
2149
+ end
2150
+
2151
+ def velocity=(v)
2152
+ raise_type_mismatch_error(v, Velocity) if v
2153
+ @velocity = v
2154
+ end
2155
+
2156
+ def spectral=(s)
2157
+ raise_type_mismatch_error(s, Spectral) if s
2158
+ @spectral = s
2159
+ end
2160
+
2161
+ def redshift=(r)
2162
+ raise_type_mismatch_error(r, Redshift) if r
2163
+ @redshift = r
2164
+ end
2165
+
2166
+ # Some or all coordinate values may be given in file
2167
+ def coord_file=(c)
2168
+ raise_type_mismatch_error(c, AstroCoordsFile) if c
2169
+ @coord_file = c
2170
+ end
2171
+
2172
+ def ==(c)
2173
+ super(c) and
2174
+ self.time == c.time and
2175
+ self.position == c.position and
2176
+ self.velocity == c.velocity and
2177
+ self.spectral == c.spectral and
2178
+ self.redshift == c.redshift and
2179
+ self.coord_file == c.coord_file
2180
+ end
2181
+ end
2182
+
2183
+ # The pixel coordinates type
2184
+ class PixelCoordsType < CoordsType
2185
+ attr_reader :coordinates
2186
+
2187
+ def coordinates=(cs)
2188
+ cs = ListOfPixelCoordinates.new(cs) if cs.class == Array
2189
+ raise_type_mismatch_error(cs, ListOfPixelCoordinates) if cs
2190
+ @coordinates = cs
2191
+ end
2192
+ end
2193
+
2194
+ # Head element of Coords group (not abstract)
2195
+ module Coords
2196
+ include SerializableToXml
2197
+ end
2198
+
2199
+ # The coordinate element consists either of a coordinate file or a sequence of time element, spatial coordinate element,
2200
+ # velocity element, and redshift element; the spatial and velocity elements may be scalar or vector; it needs to refer
2201
+ # to a coordinate system
2202
+ class AstroCoords < AstroCoordsType
2203
+ include Coords
2204
+
2205
+ def to_xml(name=nil, ns=nil)
2206
+ el = element(name, ns)
2207
+ el.attributes["#{obj_ns.prefix}:coord_system_id"] = self.coord_system_id.to_s
2208
+
2209
+ self.coordinates.each { |c| el.add_element(c.to_xml) } if self.coordinates
2210
+ el.add_element(self.time.to_xml('Time')) if self.time
2211
+ el.add_element(self.position.to_xml) if self.position
2212
+ el.add_element(self.velocity.to_xml) if self.velocity
2213
+ el.add_element(self.spectral.to_xml) if self.spectral
2214
+ el.add_element(self.redshift.to_xml) if self.redshift
2215
+ el.add_element(self.coord_file.to_xml('CoordFile')) if self.coord_file
2216
+
2217
+ collapse_namespaces(el)
2218
+
2219
+ el
2220
+ end
2221
+
2222
+ def self.from_xml(xml)
2223
+ root = element_from(xml)
2224
+
2225
+ options = {
2226
+ :coord_system_id => IdRef.new(root.attributes.get_attribute_ns(obj_ns.uri, 'coord_system_id').value),
2227
+ }
2228
+ options[:coordinates] = xml_to_obj(root, Coordinate)
2229
+ time = REXML::XPath.first(root, 'x:Time', {'x' => obj_ns.uri})
2230
+ options[:time] = TimeCoordinate.from_xml(time) if time
2231
+ options[:position] = xml_to_obj(root, Position, true)
2232
+ options[:velocity] = xml_to_obj(root, Velocity, true)
2233
+ options[:spectral] = xml_to_obj(root, Spectral, true)
2234
+ options[:redshift] = xml_to_obj(root, Redshift, true)
2235
+ coord_file = REXML::XPath.first(root, 'x:CoordFile', {'x' => obj_ns.uri})
2236
+ options[:coord_file] = AstroCoordsFile.from_xml(coord_file) if coord_file
2237
+
2238
+ AstroCoords.new(options)
2239
+ end
2240
+ end
2241
+
2242
+ # Contains pixel coordinates
2243
+ class PixelCoords < PixelCoordsType
2244
+ include Coords
2245
+
2246
+ def to_xml
2247
+ el = element()
2248
+ el.attributes["#{obj_ns.prefix}:coord_system_id"] = self.coord_system_id.to_s
2249
+
2250
+ self.coordinates.each { |c| el.add_element(c.to_xml) } if self.coordinates
2251
+
2252
+ collapse_namespaces(el)
2253
+
2254
+ el
2255
+ end
2256
+
2257
+ def self.from_xml(xml)
2258
+ root = element_from(xml)
2259
+
2260
+ options = {
2261
+ :coord_system_id => IdRef.new(root.attributes.get_attribute_ns(obj_ns.uri, 'coord_system_id').value)
2262
+ }
2263
+ options[:coordinates] = xml_to_obj(root, PixelCoordinate)
2264
+
2265
+ PixelCoords.new(options)
2266
+ end
2267
+ end
2268
+
2269
+ end
2270
+ end
2271
+ end
2272
+ end