<resource schema="ivoa" resdir="__system">
	<meta name="creationDate">2021-11-11T13:00:00</meta>
	<meta name="subject">observational-astronomy</meta>
	<meta name="subject">interferometry</meta>
	<meta name="description">The ObsCore extension for visibilities as
		resulting from interferometric observations.  This is still
		in intensive development; do not expect this to be stable.
	</meta>

<!-- The following column definitions are created by machine from the
definitions in git@github.com:ivoa/ObsCoreExtensionForVisibilityData.git.
As of July 2022, there's clearly quite some work to do to improve the
column metadata; but that should be done in the spec.

I've quickly hacked this program to do that:

import sys

names = "name desc utype ucd unit".split()
type_excs = {"ant_number": "smallint"}

for raw in sys.stdin.read().split("\\cr"):
	if not raw.strip():
		continue

	cleaned = re.sub("[{}]\s*", "",
		re.sub(r"\\[a-zA-Z]+\s*", "", 
		raw.replace("\\_", "_")))
	rec = dict(zip(names, [s.strip() for s in cleaned.split("\x26")]))

	rec["type"] = type_excs.get(rec["name"], "real")
	if rec.get("unit"):
		rec["unit"] = 'unit="{}" '.format(rec["unit"])
	else:
		rec["unit"] = ""

	print('<column name="{name}" type="{type}"\n'
		'  {unit}ucd="{ucd}"\n'
		'  utype="{utype}"\n'
		'  description="{desc}"\n/>'.format(**rec))
-->

	<STREAM id="obs-vis-columns">
		<doc>
			The columns of the ObsCore visibility extension.  You may want
			to replay this into a table intended to mix in //obs-vis#publish
			-- but there is nothing wrong with not doing so.
		</doc>

		<column original="//obscore#ObsCore.obs_publisher_did"/>

		<!-- FIRST... Two extra columns I'm taking from Mattia... -->

		<column name="antennaset" type="text"
			ucd="TODO"
			description="TODO"/>
		<column name="stations" type="text"
			ucd="TODO"
			description="TODO"/>

		<column name="s_resolution_min" type="real"
			unit="arcsec" ucd="pos.AngResolution;stat.min"
			utype="Char.SpatialAxis.Resolution.Bounds.Limits.LoLim"
			description="Angular resolution, longest baseline and  max frequency dependant"
		/>
		<column name="s_resolution_max" type="real"
			unit="arcsec" ucd="pos.AngResolution;stat.max"
			utype="Char.SpatialAxis.Resolution.Bounds.Limits.HiLim"
			description="Angular resolution, longest baseline and min frequency dependant"
		/>
		<column name="s_fov_min" type="real"
			unit="deg" ucd="phys.angSize;instr.fov;stat.min"
			utype="Char.SpatialAxis.Coverage.Bounds.Extent.LowLim"
			description="field of view diameter,  min value, max frequency dependant"
		/>
		<column name="s_fov_max" type="real"
			unit="deg" ucd="phys.angSize;instr.fov;stat.max"
			utype="Char.SpatialAxis.Coverage.Bounds.Extent.HiLim"
			description="field of view diameter,  max value, min frequency dependant"
		/>
		<column name="s_maximum_angular_scale" type="real"
			unit="arcsec" ucd="phys.angSize;stat.max"
			utype="Char.SpatialAxis.Resolution.Scale.Limits.HiLim"
			description="maximum scale in dataset, shortest baseline and  frequency dependant"
		/>
		<column name="f_min" type="real"
			unit="MHz" ucd="em.freq;stat.min"
			utype="Char.SpectralAxis.Coverage.BoundsLimits.LoLim"
			description="spectral coverage min in frequency"
		/>
		<column name="f_max" type="real"
			unit="MHz" ucd="em.freq;stat.max"
			utype="Char.SpectralAxis.Coverage.BoundsLimits.HiLim"
			description="spectral coverage max in frequency"
		/>
		<column name="t_exp_min" type="real"
			unit="s" ucd="time.duration;obs.exposure;stat.min"
			utype="Char.TimeAxis.Sampling.ExtentLoLim"
			description="minimum integration time per sample"
		/>
		<column name="t_exp_max" type="real"
			unit="s" ucd="time.duration;obs.exposure;stat.max"
			utype="Char.TimeAxis.Sampling.ExtentHiLim"
			description="maximum integration time per sample"
		/>
		<column name="uv_distance_min" type="real"
			unit="m" ucd="stat.fourier;pos;stat.min"
			utype="Char.UVAxis.Coverage.Bounds.Limits.LoLim"
			description="minimal distance in uv plane"
		/>
		<column name="uv_distance_max" type="real"
			unit="m" ucd="stat.fourier;pos;stat.max"
			utype="Char.UVAxis.Coverage.Bounds.Limits.LoLim"
			description="maximal distance in uv plane"
		/>
		<column name="uv_distribution_exc" type="real"
			ucd="stat.fourier;pos"
			utype="Char.UVAxis.Coverage.Bounds.Excentricity"
			description="excentricity of uv distribution"
		/>
		<column name="uv_distribution_fill" type="real"
			ucd="stat.fourier;pos"
			utype="Char.UVAxis.Coverage.Bounds.FillingFactor"
			description="filling factor of uv distribution"
		/>
		<column name="ant_number" type="real"
			ucd="instr.baseline;meta.number"
			utype="Provenance.ObsConfig.Instrument.Array.AntNumber"
			description="number of antennae in array"
		/>
		<column name="instrument_ant_min_dist" type="real"
			unit="m" ucd="instr.baseline;stat.min"
			utype="Provenance.ObsConfig.Instrument.Array.MinDist"
			description="minimum distance between antennae in array"
		/>
		<column name="instrument_ant_max_dist" type="real"
			unit="m" ucd="instr.baseline;stat.max"
			utype="Provenance.ObsConfig.Instrument.Array.MaxDist"
			description="maximum distance between antennae in array"
		/>
		<column name="instrument_ant_diameter" type="real"
			unit="m" ucd="instr"
			utype="Provenance.ObsConfig.Instrument.Array.Diameter"
			description="diameter of antennae in array"
		/>
		<column name="uv_distribution_map" type="real"
			ucd="stat.fourier;pos"
			utype="Char.UVAxis.Sampling.Sensitivity.Map"
			description="uv distribution map"
		/>
		<column name="s_resolution_beam_dirty" type="real"
			ucd="pos.AngResolution"
			utype="Char.SpatialAxis.Resolution.Variability.DirtyBeam.Map"
			description="dirty beam"
		/>
	</STREAM>


	<!-- TODO: from here on, there's terribly much duplication
	with //obscore.  At the latest when we have a second obscore
	extension, we will have to refactor this. -->

	<table id="obsvis" adql="True" onDisk="True" system="True">
		<property key="supportsModel">Obsvis-1.0</property>
		<property key="supportsModelURI"
			>ivo://ivoa.net/std/TODO</property>
		<property key="forceStats">True</property>

		<primary>obs_publisher_did</primary>
		<foreignKey inTable="//obscore#ObsCore"
			source="obs_publisher_did"/>
		
		<meta name="description">An IVOA-defined metadata table for
			radio-interferometric measurements ("visibilities").  You
			will almost always want to join this table to ivoa.obscore
			using the obs_publisher_did column.</meta>

		<FEED source="obs-vis-columns"/>

		<!-- bogus view creation statetment; the view is actually created
		from the _obsvissources table.  It's here only so there is something
		when initially importing things.-->

		<viewStatement>create view \qName (missing) AS (SELECT 0)</viewStatement>
	</table>

	<data id="create" updating="True" auto="False">
		<!-- the view is created from prescriptions in _obsvissources -->
		<make table="obsvis">
			<script type="postCreation" lang="python" id="createObsvisView">
				try:
					from gavo import rsc
					ocTable = rsc.TableForDef(
						base.resolveCrossId("//obsvis#_obsvissources"),
						connection=table.connection)
					parts = ["(%s)"%row["sqlFragment"]
						for row in ocTable.iterQuery(ocTable.tableDef, "")]
					if parts:
						table.connection.execute(
							"DROP VIEW IF EXISTS ivoa.obsvis")
						table.connection.execute(
							"CREATE VIEW ivoa.obsvis AS (%s)"%(
							" UNION ALL ".join(parts)))
						rsc.TableForDef(
							base.resolveCrossId("//obsvis#obsvis"),
							connection=table.connection).updateMeta()
				except:
					base.ui.notifyError(
						"--------  Try   dachs imp //obsvis recover to fix this -------")
					raise
			</script>
		<!--
			<script name="unpublish obsvis DM" type="beforeDrop" lang="SQL">
					delete from tap_schema.supportedmodels
						where dmivorn ilike 'ivo://ivoa.net/std/obscore#%'
			</script> -->
		</make>
	</data>

	<table id="_obsvissources" onDisk="True"
			dupePolicy="overwrite" primary="tableName" system="True">
		<meta name="description">
			This table contains the SQL fragments that make up this site's
			ivoa.obsvis view.

			Manipulate this table through gavo imp on tables that have an obsvis
			mixin, or by dropping RDs or purging tables that are part of obscore.
		</meta>
		<column name="tableName" type="text"
			description="Name of the table that generated this fragment (usually
				through a mixin)"/>
		<column name="sqlFragment" type="text"
			description="The SQL fragment contributed from tableName"/>
		<column name="sourcerd" type="text"
			description="The RD the table was found in at input (this is
				mainly to support dachs drop -f"/>
	</table>

	<data id="init">
		<make table="_obsvissources"/>
	</data>


	<mixinDef id="publish">
		<doc>
			Mix this into a table that contains visibilities to have their
			metadata show up in the obscore extension for visibilities.  This
			only makes sense together with one of the obscore mixins, as
			the main metadata is kept in the obscore table.
			
			Use the parameters to map whatever is in your table to obsvis'
			columns.  As with obscore, parameter values must be SQL expressions
			evaluatable within the table mixed in.  The default is to copy
			every column from an identically-named column.  Hence, you will
			have to map anything you don't have to NULL manually.
		</doc>
		<LOOP>
			<codeItems>
				# for now, just create parameters from the extension table's
				# columns; this probably will need work even if their
				# descriptions are fixed.
				for col in context.getById("obsvis"):
					yield {"name": col.name, "description": col.description}
			</codeItems>
			<events>
				<mixinPar name="\name" description="\description">\name</mixinPar>
			</events>
		</LOOP>

		<events>
			<LOOP>
				<codeItems>
				items = []
				for col in context.getById("obsvis"):
					items.append(r"CAST(\{name} AS {type}) AS {name}".format(
						**col.asInfoDict()))
				yield {"clause": ",\n".join(items)}
				</codeItems>
				<events>
					<property name="obsvisClause">\clause</property>
				</events>
			</LOOP>

			<script id="addTableToObsvisSources"
					lang="python" type="afterMeta">
				obsvisClause = table.tableDef.expand(
					table.tableDef.getProperty("obsvisClause"))
				from gavo import rsc
				ots = rsc.TableForDef(
					base.resolveCrossId("//obsvis#_obsvissources"),
					connection=table.connection)
				ots.addRow({"tableName": table.tableDef.getQName(),
					"sourcerd": table.tableDef.rd.sourceId,
					"sqlFragment": "SELECT %s FROM %s"%(
						obsvisClause, table.tableDef.getQName())})
			</script>
			
			<script original="//obsvis#createObsvisView" name="createObsvisView"/>

			<script id="removeTableFromObsvisSources" 
					lang="python" 
					type="beforeDrop">
				from gavo import rsc
				if table.getTableType("ivoa._obsvissources"):
					table.connection.execute(
						"DELETE FROM ivoa._obsvissources WHERE tableName=%(name)s",
						{"name": table.tableDef.getQName()})
					# importing this table may take a long time, and we don't want
					# to have obscore offline for so long; so, we immediately recreate
					# it.
					rsc.makeData(base.resolveCrossId("//obsvis#create"),
						connection=table.connection)
					table.connection.commit()
			</script>

		</events>
	</mixinDef>

</resource>
