def normalize_radec_str(ra_str, dec_str): if ra_str is None or ra_str == '': ra = ra_str else: ra = wcs.raDegToString(wcs.hmsStrToDeg(ra_str)) if dec_str is None or dec_str == '': dec = dec_str else: dec = wcs.decDegToString(wcs.dmsStrToDeg(dec_str)) return (ra, dec)
def set_pointing_cb(self): try: ra = self.w.ra.get_text() dec = self.w.dec.get_text() tgt_ra_deg = wcs.hmsStrToDeg(ra) tgt_dec_deg = wcs.dmsStrToDeg(dec) self.ctr_ra_deg, self.ctr_dec_deg = tgt_ra_deg, tgt_dec_deg self.w.pra.set_text(ra) self.w.pdec.set_text(dec) except Exception as e: self.fv.show_error(str(e))
def search(self, dstpath, **params): """For compatibility with generic image catalog search. TODO: dstpath provides the pathname for storing the image """ self.logger.debug("search params=%s" % (str(params))) ra, dec = params['ra'], params['dec'] if not (':' in ra): # Assume RA and DEC are in degrees ra_deg = float(ra) dec_deg = float(dec) else: # Assume RA and DEC are in standard string notation ra_deg = wcs.hmsStrToDeg(ra) dec_deg = wcs.dmsStrToDeg(dec) # Convert to degrees for search wd_deg = float(params['width']) / 60.0 ht_deg = float(params['height']) / 60.0 ## wd_deg = float(params['width']) ## ht_deg = float(params['height']) # initialize our query object with the service's base URL query = pyvo.sia.SIAQuery(self.url) query.ra = ra_deg query.dec = dec_deg query.size = (wd_deg, ht_deg) query.format = 'image/fits' self.logger.info("Will query: %s" % query.getqueryurl(True)) results = query.execute() if len(results) > 0: self.logger.info("Found %d images" % len(results)) else: self.logger.warn("Found no images in this area" % len(results)) return None # For now, we pick the first one found # REQUIRES FIX IN PYVO: # imfile = results[0].cachedataset(dir="/tmp") # # Workaround: fitspath = results[0].make_dataset_filename(dir="/tmp") results[0].cachedataset(fitspath) # explicit return return fitspath
def search(self, dstpath, **params): """For compatibility with generic image catalog search. TODO: dstpath provides the pathname for storing the image """ self.logger.debug("search params=%s" % (str(params))) ra, dec = params['ra'], params['dec'] if not (':' in ra): # Assume RA and DEC are in degrees ra_deg = float(ra) dec_deg = float(dec) else: # Assume RA and DEC are in standard string notation ra_deg = wcs.hmsStrToDeg(ra) dec_deg = wcs.dmsStrToDeg(dec) # Convert to degrees for search wd_deg = float(params['width']) / 60.0 ht_deg = float(params['height']) / 60.0 ## wd_deg = float(params['width']) ## ht_deg = float(params['height']) # initialize our query object with the service's base URL query = pyvo.sia.SIAQuery(self.url) query.ra = ra_deg query.dec = dec_deg query.size = (wd_deg, ht_deg) query.format = 'image/fits' self.logger.info("Will query: %s" % query.getqueryurl(True)) results = query.execute() if len(results) > 0: self.logger.info("Found %d images" % len(results)) else: self.logger.warning("Found no images in this area" % len(results)) return None # For now, we pick the first one found # REQUIRES FIX IN PYVO: # imfile = results[0].cachedataset(dir="/tmp") # # Workaround: fitspath = results[0].make_dataset_filename(dir="/tmp") results[0].cachedataset(fitspath) # explicit return return fitspath
def search(self, dstpath, **params): """For compatibility with generic image catalog search. TODO: dstpath provides the pathname for storing the image """ self.logger.debug("search params=%s" % (str(params))) ra, dec = params['ra'], params['dec'] if not (':' in ra): # Assume RA and DEC are in degrees ra_deg = float(ra) dec_deg = float(dec) else: # Assume RA and DEC are in standard string notation ra_deg = wcs.hmsStrToDeg(ra) dec_deg = wcs.dmsStrToDeg(dec) # Convert to degrees for search wd_deg = float(params['width']) / 60.0 ht_deg = float(params['height']) / 60.0 # Note requires astropy 3.x+ c = coordinates.SkyCoord(ra_deg * units.degree, dec_deg * units.degree, frame='icrs') self.logger.info("Querying catalog: %s" % (self.full_name)) time_start = time.time() results = self.querymod.get_image_list( c, image_width=wd_deg * units.degree, image_height=ht_deg * units.degree) time_elapsed = time.time() - time_start if len(results) > 0: self.logger.info("Found %d images" % len(results)) else: self.logger.warn("Found no images in this area" % len(results)) return None # For now, we pick the first one found url = results[0] #fitspath = results[0].make_dataset_filename(dir="/tmp") # TODO: download file fitspath = url # explicit return return fitspath
def search(self, dstpath, **params): """For compatibility with generic image catalog search. TODO: dstpath provides the pathname for storing the image """ self.logger.debug("search params=%s" % (str(params))) ra, dec = params['ra'], params['dec'] if not (':' in ra): # Assume RA and DEC are in degrees ra_deg = float(ra) dec_deg = float(dec) else: # Assume RA and DEC are in standard string notation ra_deg = wcs.hmsStrToDeg(ra) dec_deg = wcs.dmsStrToDeg(dec) # Convert to degrees for search wd_deg = float(params['width']) / 60.0 ht_deg = float(params['height']) / 60.0 # Note requires astropy 3.x+ c = coordinates.SkyCoord(ra_deg * units.degree, dec_deg * units.degree, frame='icrs') self.logger.info("Querying catalog: %s" % (self.full_name)) time_start = time.time() results = self.querymod.get_image_list(c, image_width=wd_deg * units.degree, image_height=ht_deg * units.degree) time_elapsed = time.time() - time_start if len(results) > 0: self.logger.info("Found %d images" % len(results)) else: self.logger.warning("Found no images in this area" % len(results)) return None # For now, we pick the first one found url = results[0] #fitspath = results[0].make_dataset_filename(dir="/tmp") # TODO: download file fitspath = url # explicit return return fitspath
def set_pan_cb(self, *args): idx = self.w.pan_coord.get_index() pan_coord = self.pancoord_options[idx] pan_xs = self.w.pan_x.get_text().strip() pan_ys = self.w.pan_y.get_text().strip() # TODO: use current value for other coord if only one coord supplied if (':' in pan_xs) or (':' in pan_ys): # TODO: get maximal precision pan_x = wcs.hmsStrToDeg(pan_xs) pan_y = wcs.dmsStrToDeg(pan_ys) pan_coord = 'wcs' else: coord_offset = self.fv.settings.get('pixel_coords_offset', 0.0) pan_x = float(pan_xs) - coord_offset pan_y = float(pan_ys) - coord_offset self.fitsimage.set_pan(pan_x, pan_y, coord=pan_coord) return True
def search(self, dstpath, **params): """For compatibility with generic image catalog search.""" self.logger.debug("search params=%s" % (str(params))) ra, dec = params['ra'], params['dec'] if not (':' in ra): # Assume RA and DEC are in degrees ra_deg = float(ra) dec_deg = float(dec) else: # Assume RA and DEC are in standard string notation ra_deg = wcs.hmsStrToDeg(ra) dec_deg = wcs.dmsStrToDeg(dec) # Convert to degrees for search wd_deg = float(params['width']) / 60.0 ht_deg = float(params['height']) / 60.0 # Note requires astropy 3.x+ c = coord.SkyCoord(ra_deg * u.degree, dec_deg * u.degree, frame='icrs') self.logger.info("Querying catalog: %s" % (self.full_name)) results = self.querymod.get_image_list(c, self.survey, width=wd_deg * u.degree, height=ht_deg * u.degree, pixels=(1200, 1200), deedger="_skip_") if len(results) > 0: self.logger.info("Found %d images" % len(results)) else: self.logger.warning("Found no images in this area" % len(results)) return None # For now, we pick the first one found url = results[0] # fitspath = results[0].make_dataset_filename(dir="/tmp") # download file self.fetch(url, filepath=dstpath) # explicit return return dstpath
def set_pan_cb(self, *args): idx = self.w.pan_coord.get_index() pan_coord = self.pancoord_options[idx] pan_xs = self.w.pan_x.get_text().strip() pan_ys = self.w.pan_y.get_text().strip() # TODO: use current value for other coord if only one coord supplied if (':' in pan_xs) or (':' in pan_ys): # TODO: get maximal precision pan_x = wcs.hmsStrToDeg(pan_xs) pan_y = wcs.dmsStrToDeg(pan_ys) pan_coord = 'wcs' elif pan_coord == 'wcs': pan_x = float(pan_xs) pan_y = float(pan_ys) else: coord_offset = self.fv.settings.get('pixel_coords_offset', 0.0) pan_x = float(pan_xs) - coord_offset pan_y = float(pan_ys) - coord_offset self.fitsimage.set_pan(pan_x, pan_y, coord=pan_coord) return True
def add_target_cb(self): ra = self.w.ra.get_text() dec = self.w.dec.get_text() eq = self.w.equinox.get_text() name = self.w.name.get_text() try: tgt_ra_deg = wcs.hmsStrToDeg(ra) tgt_dec_deg = wcs.dmsStrToDeg(dec) tgt_equinox = float(eq) # TODO: use 'target' entity from qplan? tgt = Bunch.Bunch(ra_deg=tgt_ra_deg, dec_deg=tgt_dec_deg, equinox=tgt_equinox, name=name) self.targets.append(tgt) self.draw_targets() except Exception as e: errmsg = "Failed to process target: %s" % (str(e)) self.fv.show_error(errmsg) return True
def search(self, **params): """For compatibility with generic star catalog search. """ self.logger.debug("search params=%s" % (str(params))) ra, dec = params['ra'], params['dec'] if not (':' in ra): # Assume RA and DEC are in degrees ra_deg = float(ra) dec_deg = float(dec) else: # Assume RA and DEC are in standard string notation ra_deg = wcs.hmsStrToDeg(ra) dec_deg = wcs.dmsStrToDeg(dec) # Convert to degrees for search radius radius_deg = float(params['r']) / 60.0 #radius_deg = float(params['r']) # initialize our query object with the service's base URL query = pyvo.scs.SCSQuery(self.url) query.ra = ra_deg query.dec = dec_deg query.radius = radius_deg self.logger.info("Will query: %s" % query.getqueryurl(True)) time_start = time.time() results = query.execute() time_elapsed = time.time() - time_start numsources = len(results) self.logger.info("Found %d sources in %.2f sec" % ( numsources, time_elapsed)) # Scan the returned fields to find ones we need to extract # particulars from (ra, dec, id, magnitude) mags = [] ext = {} fields = results.fielddesc() for field in fields: ucd = str(field.ucd).lower() if ucd == 'id_main': ext['id'] = field.name elif ucd == 'pos_eq_ra_main': ext['ra'] = field.name elif ucd == 'pos_eq_dec_main': ext['dec'] = field.name if ('phot_' in ucd) or ('phot.' in ucd): mags.append(field.name) self.logger.debug("possible magnitude fields: %s" % str(mags)) if len(mags) > 0: magfield = mags[0] else: magfield = None self.logger.info("Found %d sources" % len(results)) starlist = [] for source in results: data = dict(source.items()) starlist.append(self.toStar(data, ext, magfield)) # metadata about the list columns = [('Name', 'name'), ('RA', 'ra'), ('DEC', 'dec'), ('Mag', 'mag'), ('Preference', 'preference'), ('Priority', 'priority'), ('Description', 'description'), ] # Append extra columns returned by search to table header cols = list(source.keys()) cols.remove(ext['ra']) cols.remove(ext['dec']) cols.remove(ext['id']) columns.extend(zip(cols, cols)) # which column is the likely one to color source circles colorCode = 'Mag' info = Bunch.Bunch(columns=columns, color=colorCode) return starlist, info
def search(self, **params): """For compatibility with generic star catalog search. """ self.logger.debug("search params=%s" % (str(params))) ra, dec = params['ra'], params['dec'] if not (':' in ra): # Assume RA and DEC are in degrees ra_deg = float(ra) dec_deg = float(dec) else: # Assume RA and DEC are in standard string notation ra_deg = wcs.hmsStrToDeg(ra) dec_deg = wcs.dmsStrToDeg(dec) # Convert to degrees for search radius radius_deg = float(params['r']) / 60.0 #radius_deg = float(params['r']) # Note requires astropy 3.x+ c = coordinates.SkyCoord(ra_deg * units.degree, dec_deg * units.degree, frame='icrs') self.logger.info("Querying catalog: %s" % (self.full_name)) time_start = time.time() results = conesearch.conesearch(c, radius_deg * units.degree, catalog_db=self.full_name) time_elapsed = time.time() - time_start numsources = results.array.size self.logger.info("Found %d sources in %.2f sec" % ( numsources, time_elapsed)) # Scan the returned fields to find ones we need to extract # particulars from (ra, dec, id, magnitude) mags = [] ext = {} fields = results.array.dtype.names for name in fields: ucd = results.get_field_by_id(name).ucd ucd = str(ucd).lower() if ucd == 'id_main': ext['id'] = name elif ucd == 'pos_eq_ra_main': ext['ra'] = name elif ucd == 'pos_eq_dec_main': ext['dec'] = name if ('phot_' in ucd) or ('phot.' in ucd): mags.append(name) self.logger.debug("possible magnitude fields: %s" % str(mags)) if len(mags) > 0: magfield = mags[0] else: magfield = None # prepare the result list starlist = [] arr = results.array for i in range(numsources): source = dict(zip(fields, arr[i])) starlist.append(self.toStar(source, ext, magfield)) # metadata about the list columns = [('Name', 'name'), ('RA', 'ra'), ('DEC', 'dec'), ('Mag', 'mag'), ('Preference', 'preference'), ('Priority', 'priority'), ('Description', 'description'), ] # Append extra columns returned by search to table header # TODO: what if not all sources have same record structure? # is this possible with VO? cols = list(fields) cols.remove(ext['ra']) cols.remove(ext['dec']) cols.remove(ext['id']) columns.extend(zip(cols, cols)) # which column is the likely one to color source circles colorCode = 'Mag' info = Bunch.Bunch(columns=columns, color=colorCode) return starlist, info
def search(self, **params): self.logger.debug("search params=%s" % (str(params))) url = self.base_url % params data = self.fetch(url, filepath=None) data = data.decode("utf8") lines = data.split('\n') offset = 0 while offset < len(lines): line = lines[offset].strip() # print(line) offset += 1 if line.startswith('-'): break self.logger.debug("offset=%d" % (offset)) results = [] table = [lines[offset - 2]] for line in lines[offset:]: line = line.strip() # print(line) if (len(line) == 0) or line.startswith('#'): continue elts = line.split() if (len(elts) < 3): continue table.append(line) try: name = elts[self.index['name']] ra = elts[self.index['ra']] dec = elts[self.index['dec']] mag = float(elts[self.index['mag']]) # print(name) if (self.format == 'deg') or not (':' in ra): # Assume RA and DEC are in degrees ra_deg = float(ra) dec_deg = float(dec) else: # Assume RA and DEC are in standard string notation ra_deg = wcs.hmsStrToDeg(ra) dec_deg = wcs.dmsStrToDeg(dec) # convert ra/dec via EQUINOX change if catalog EQUINOX is # not the same as our default one (2000) if self.equinox != 2000.0: ra_deg, dec_deg = wcs.eqToEq2000(ra_deg, dec_deg, self.equinox) ra_txt = wcs.raDegToString(ra_deg, format='%02d:%02d:%06.3f') dec_txt = wcs.decDegToString(dec_deg, format='%s%02d:%02d:%05.2f') self.logger.debug("STAR %s AT ra=%s dec=%s mag=%f" % (name, ra_txt, dec_txt, mag)) results.append( Star(name=name, ra_deg=ra_deg, dec_deg=dec_deg, ra=ra_txt, dec=dec_txt, mag=mag, preference=0.0, priority=0, description='')) except Exception as e: self.logger.error("Error parsing catalog query results: %s" % (str(e))) # metadata about the list columns = [ ('Name', 'name'), ('RA', 'ra'), ('DEC', 'dec'), ('Mag', 'mag'), ('Preference', 'preference'), ('Priority', 'priority'), ('Description', 'description'), ] info = Bunch.Bunch(columns=columns, color='Mag') return (results, info)
def search(self, **params): """ For compatibility with generic star catalog search. """ self.logger.debug("search params=%s" % (str(params))) ra, dec = params['ra'], params['dec'] if not (':' in ra): # Assume RA and DEC are in degrees ra_deg = float(ra) dec_deg = float(dec) else: # Assume RA and DEC are in standard string notation ra_deg = wcs.hmsStrToDeg(ra) dec_deg = wcs.dmsStrToDeg(dec) # Convert to degrees for search radius radius_deg = float(params['r']) / 60.0 # radius_deg = float(params['r']) # initialize our query object with the service's base URL query = pyvo.scs.SCSQuery(self.url) query.ra = ra_deg query.dec = dec_deg query.radius = radius_deg self.logger.info("Will query: %s" % query.getqueryurl(True)) time_start = time.time() results = query.execute() time_elapsed = time.time() - time_start numsources = len(results) self.logger.info("Found %d sources in %.2f sec" % (numsources, time_elapsed)) # Scan the returned fields to find ones we need to extract # particulars from (ra, dec, id, magnitude) mags = [] ext = {} fields = results.fielddesc() for field in fields: ucd = str(field.ucd).lower() if ucd == 'id_main': ext['id'] = field.name elif ucd == 'pos_eq_ra_main': ext['ra'] = field.name elif ucd == 'pos_eq_dec_main': ext['dec'] = field.name if ('phot_' in ucd) or ('phot.' in ucd): mags.append(field.name) self.logger.debug("possible magnitude fields: %s" % str(mags)) if len(mags) > 0: magfield = mags[0] else: magfield = None self.logger.info("Found %d sources" % len(results)) starlist = [] for source in results: data = dict(source.items()) starlist.append(self.toStar(data, ext, magfield)) # metadata about the list columns = [ ('Name', 'name'), ('RA', 'ra'), ('DEC', 'dec'), ('Mag', 'mag'), ('Preference', 'preference'), ('Priority', 'priority'), ('Description', 'description'), ] # Append extra columns returned by search to table header cols = list(source.keys()) cols.remove(ext['ra']) cols.remove(ext['dec']) cols.remove(ext['id']) columns.extend(zip(cols, cols)) # which column is the likely one to color source circles colorCode = 'Mag' info = Bunch.Bunch(columns=columns, color=colorCode) return starlist, info
def search(self, **params): self.logger.debug("search params=%s" % (str(params))) url = self.base_url % params data = self.fetch(url, filepath=None) data = data.decode("utf8") lines = data.split("\n") offset = 0 while offset < len(lines): line = lines[offset].strip() print(line) offset += 1 if line.startswith("-"): break self.logger.debug("offset=%d" % (offset)) results = [] table = [lines[offset - 2]] for line in lines[offset:]: line = line.strip() # print ">>>", line if (len(line) == 0) or line.startswith("#"): continue elts = line.split() if len(elts) < 3: continue table.append(line) try: name = elts[self.index["name"]] ra = elts[self.index["ra"]] dec = elts[self.index["dec"]] mag = float(elts[self.index["mag"]]) # print name if (self.format == "deg") or not (":" in ra): # Assume RA and DEC are in degrees ra_deg = float(ra) dec_deg = float(dec) else: # Assume RA and DEC are in standard string notation ra_deg = wcs.hmsStrToDeg(ra) dec_deg = wcs.dmsStrToDeg(dec) # convert ra/dec via EQUINOX change if catalog EQUINOX is # not the same as our default one (2000) if cmp(self.equinox, 2000.0) != 0: ra_deg, dec_deg = wcs.eqToEq2000(ra_deg, dec_deg, self.equinox) ra_txt = wcs.raDegToString(ra_deg, format="%02d:%02d:%06.3f") dec_txt = wcs.decDegToString(dec_deg, format="%s%02d:%02d:%05.2f") self.logger.debug("STAR %s AT ra=%s dec=%s mag=%f" % (name, ra_txt, dec_txt, mag)) results.append( Star( name=name, ra_deg=ra_deg, dec_deg=dec_deg, ra=ra_txt, dec=dec_txt, mag=mag, preference=0.0, priority=0, description="", ) ) except Exception as e: print(str(e)) self.logger.error("Error parsing catalog query results: %s" % (str(e))) # metadata about the list columns = [ ("Name", "name"), ("RA", "ra"), ("DEC", "dec"), ("Mag", "mag"), ("Preference", "preference"), ("Priority", "priority"), ("Description", "description"), ] info = Bunch.Bunch(columns=columns, color="Mag") return (results, info)
def search(self, **params): self.logger.debug("search params=%s" % (str(params))) url = self.base_url % params data = self.fetch(url, filepath=None) data = data.decode("utf8") lines = data.split('\n') offset = 0 while offset < len(lines): line = lines[offset].strip() #print(line) offset += 1 if line.startswith('-'): break self.logger.debug("offset=%d" % (offset)) results = [] table = [lines[offset-2]] for line in lines[offset:]: line = line.strip() #print(line) if (len(line) == 0) or line.startswith('#'): continue elts = line.split() if (len(elts) < 3): continue table.append(line) try: name = elts[self.index['name']] ra = elts[self.index['ra']] dec = elts[self.index['dec']] mag = float(elts[self.index['mag']]) #print name if (self.format == 'deg') or not (':' in ra): # Assume RA and DEC are in degrees ra_deg = float(ra) dec_deg = float(dec) else: # Assume RA and DEC are in standard string notation ra_deg = wcs.hmsStrToDeg(ra) dec_deg = wcs.dmsStrToDeg(dec) # convert ra/dec via EQUINOX change if catalog EQUINOX is # not the same as our default one (2000) if cmp(self.equinox, 2000.0) != 0: ra_deg, dec_deg = wcs.eqToEq2000(ra_deg, dec_deg, self.equinox) ra_txt = wcs.raDegToString(ra_deg, format='%02d:%02d:%06.3f') dec_txt = wcs.decDegToString(dec_deg, format='%s%02d:%02d:%05.2f') self.logger.debug("STAR %s AT ra=%s dec=%s mag=%f" % ( name, ra_txt, dec_txt, mag)) results.append(Star(name=name, ra_deg=ra_deg, dec_deg=dec_deg, ra=ra_txt, dec=dec_txt, mag=mag, preference=0.0, priority=0, description='')) except Exception as e: self.logger.error("Error parsing catalog query results: %s" % ( str(e))) # metadata about the list columns = [('Name', 'name'), ('RA', 'ra'), ('DEC', 'dec'), ('Mag', 'mag'), ('Preference', 'preference'), ('Priority', 'priority'), ('Description', 'description'), ] info = Bunch.Bunch(columns=columns, color='Mag') return (results, info)
def search(self, **params): """ For compatibility with generic star catalog search. """ self.logger.debug("search params=%s" % (str(params))) ra, dec = params['ra'], params['dec'] if not (':' in ra): # Assume RA and DEC are in degrees ra_deg = float(ra) dec_deg = float(dec) else: # Assume RA and DEC are in standard string notation ra_deg = wcs.hmsStrToDeg(ra) dec_deg = wcs.dmsStrToDeg(dec) # Convert to degrees for search radius radius_deg = float(params['r']) / 60.0 # radius_deg = float(params['r']) # Note requires astropy 0.3.x+ c = coordinates.SkyCoord(ra_deg * units.degree, dec_deg * units.degree, frame='icrs') self.logger.info("Querying catalog: %s" % (self.full_name)) time_start = time.time() with warnings.catch_warnings(): # Ignore VO warnings warnings.simplefilter('ignore') results = conesearch.conesearch(c, radius_deg * units.degree, catalog_db=self.full_name, verbose=False) time_elapsed = time.time() - time_start numsources = results.array.size self.logger.info("Found %d sources in %.2f sec" % (numsources, time_elapsed)) # Scan the returned fields to find ones we need to extract # particulars from (ra, dec, id, magnitude) mags = [] ext = {} fields = results.array.dtype.names for name in fields: ucd = results.get_field_by_id(name).ucd ucd = str(ucd).lower() if ucd == 'id_main': ext['id'] = name elif ucd == 'pos_eq_ra_main': ext['ra'] = name elif ucd == 'pos_eq_dec_main': ext['dec'] = name if ('phot_' in ucd) or ('phot.' in ucd): mags.append(name) self.logger.debug("possible magnitude fields: %s" % str(mags)) if len(mags) > 0: magfield = mags[0] else: magfield = None # prepare the result list starlist = [] arr = results.array for i in range(numsources): source = dict(zip(fields, arr[i])) starlist.append(self.toStar(source, ext, magfield)) # metadata about the list columns = [ ('Name', 'name'), ('RA', 'ra'), ('DEC', 'dec'), ('Mag', 'mag'), ('Preference', 'preference'), ('Priority', 'priority'), ('Description', 'description'), ] # Append extra columns returned by search to table header # TODO: what if not all sources have same record structure? # is this possible with VO? cols = list(fields) cols.remove(ext['ra']) cols.remove(ext['dec']) cols.remove(ext['id']) columns.extend(zip(cols, cols)) # which column is the likely one to color source circles colorCode = 'Mag' info = Bunch.Bunch(columns=columns, color=colorCode) return starlist, info