LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
firefly.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008, 2009, 2010, 2015 LSST Corporation.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 
23 from io import BytesIO
24 from socket import gaierror
25 import tempfile
26 
27 import lsst.afw.display.interface as interface
28 import lsst.afw.display.virtualDevice as virtualDevice
29 import lsst.afw.display.ds9Regions as ds9Regions
30 import lsst.afw.display as afwDisplay
31 import lsst.afw.math as afwMath
32 import lsst.log
33 
34 from .footprints import createFootprintsTable
35 
36 try:
37  import firefly_client
38  _fireflyClient = None
39 except ImportError as e:
40  raise RuntimeError("Cannot import firefly_client: %s" % (e))
41 from ws4py.client import HandshakeError
42 
43 
44 class FireflyError(Exception):
45 
46  def __init__(self, str):
47  Exception.__init__(self, str)
48 
49 
51  """Return the version of firefly_client in use, as a string"""
52  return(firefly_client.__version__)
53 
54 
55 class DisplayImpl(virtualDevice.DisplayImpl):
56  """Device to talk to a firefly display"""
57 
58  @staticmethod
59  def __handleCallbacks(event):
60  if 'type' in event['data']:
61  if event['data']['type'] == 'AREA_SELECT':
62  lsst.log.debug('*************area select')
63  pParams = {'URL': 'http://web.ipac.caltech.edu/staff/roby/demo/wise-m51-band2.fits',
64  'ColorTable': '9'}
65  plot_id = 3
66  global _fireflyClient
67  _fireflyClient.show_fits(fileOnServer=None, plot_id=plot_id, additionalParams=pParams)
68 
69  lsst.log.debug("Callback event info: {}".format(event))
70  return
71  data = dict((_.split('=') for _ in event.get('data', {}).split('&')))
72  if data.get('type') == "POINT":
73  lsst.log.debug("Event Received: %s" % data.get('id'))
74 
75  def __init__(self, display, verbose=False, url=None,
76  name=None, *args, **kwargs):
77  virtualDevice.DisplayImpl.__init__(self, display, verbose)
78 
79  if self.verbose:
80  print("Opening firefly device %s" % (self.display.frame if self.display else "[None]"))
81 
82  global _fireflyClient
83  if not _fireflyClient:
84  import os
85  start_tab = None
86  html_file = kwargs.get('html_file',
87  os.environ.get('FIREFLY_HTML', 'slate.html'))
88  if url is None:
89  if (('fireflyLabExtension' in os.environ) and
90  ('fireflyURLLab' in os.environ)):
91  url = os.environ['fireflyURLLab']
92  start_tab = kwargs.get('start_tab', True)
93  start_browser_tab = kwargs.get('start_browser_tab', False)
94  if (name is None) and ('fireflyChannelLab' in os.environ):
95  name = os.environ['fireflyChannelLab']
96  elif 'FIREFLY_URL' in os.environ:
97  url = os.environ['FIREFLY_URL']
98  else:
99  raise RuntimeError('Cannot determine url from environment; you must pass url')
100 
101  token = kwargs.get('token',
102  os.environ.get('ACCESS_TOKEN', None))
103 
104  try:
105  if start_tab:
106  if verbose:
107  print('Starting Jupyterlab client')
108  _fireflyClient = firefly_client.FireflyClient.make_lab_client(
109  start_tab=True, start_browser_tab=start_browser_tab,
110  html_file=kwargs.get('html_file'), verbose=verbose,
111  token=token)
112 
113  else:
114  if verbose:
115  print('Starting vanilla client')
116  _fireflyClient = firefly_client.FireflyClient.make_client(
117  url=url, html_file=html_file, launch_browser=True,
118  channel_override=name, verbose=verbose,
119  token=token)
120 
121  except (HandshakeError, gaierror) as e:
122  raise RuntimeError("Unable to connect to %s: %s" % (url or '', e))
123 
124  try:
125  _fireflyClient.add_listener(self.__handleCallbacks__handleCallbacks)
126  except Exception as e:
127  raise RuntimeError("Cannot add listener. Browser must be connected" +
128  "to %s: %s" %
129  (_fireflyClient.get_firefly_url(), e))
130 
131  self._isBuffered_isBuffered = False
132  self._regions_regions = []
133  self._regionLayerId_regionLayerId = self._getRegionLayerId_getRegionLayerId()
134  self._fireflyFitsID_fireflyFitsID = None
135  self._fireflyMaskOnServer_fireflyMaskOnServer = None
136  self._client_client = _fireflyClient
137  self._channel_channel = _fireflyClient.channel
138  self._url_url = _fireflyClient.get_firefly_url()
139  self._maskIds_maskIds = []
140  self._maskDict_maskDict = {}
141  self._maskPlaneColors_maskPlaneColors = {}
142  self._maskTransparencies_maskTransparencies = {}
143  self._lastZoom_lastZoom = None
144  self._lastPan_lastPan = None
145  self._lastStretch_lastStretch = None
146 
147  def _getRegionLayerId(self):
148  return "lsstRegions%s" % self.display.frame if self.display else "None"
149 
150  def _clearImage(self):
151  """Delete the current image in the Firefly viewer
152  """
153  self._client_client.dispatch(action_type='ImagePlotCntlr.deletePlotView',
154  payload=dict(plotId=str(self.display.frame)))
155 
156  def _mtv(self, image, mask=None, wcs=None, title=""):
157  """Display an Image and/or Mask on a Firefly display
158  """
159  if title == "":
160  title = str(self.display.frame)
161  if image:
162  if self.verbose:
163  print('displaying image')
164  self._erase_erase()
165 
166  with tempfile.NamedTemporaryFile() as fd:
167  afwDisplay.writeFitsImage(fd.name, image, wcs, title)
168  fd.flush()
169  fd.seek(0, 0)
170  self._fireflyFitsID_fireflyFitsID = _fireflyClient.upload_data(fd, 'FITS')
171 
172  try:
173  viewer_id = ('image-' + str(_fireflyClient.render_tree_id) + '-' +
174  str(self.frame))
175  except AttributeError:
176  viewer_id = 'image-' + str(self.frame)
177  extraParams = dict(Title=title,
178  MultiImageIdx=0,
179  PredefinedOverlayIds=' ',
180  viewer_id=viewer_id)
181  # Firefly's Javascript API requires a space for parameters;
182  # otherwise the parameter will be ignored
183 
184  if self._lastZoom_lastZoom:
185  extraParams['InitZoomLevel'] = self._lastZoom_lastZoom
186  extraParams['ZoomType'] = 'LEVEL'
187  if self._lastPan_lastPan:
188  extraParams['InitialCenterPosition'] = '{0:.3f};{1:.3f};PIXEL'.format(
189  self._lastPan_lastPan[0], self._lastPan_lastPan[1])
190  if self._lastStretch_lastStretch:
191  extraParams['RangeValues'] = self._lastStretch_lastStretch
192 
193  ret = _fireflyClient.show_fits(self._fireflyFitsID_fireflyFitsID, plot_id=str(self.display.frame),
194  **extraParams)
195 
196  if not ret["success"]:
197  raise RuntimeError("Display of image failed")
198 
199  if mask:
200  if self.verbose:
201  print('displaying mask')
202  with tempfile.NamedTemporaryFile() as fdm:
203  afwDisplay.writeFitsImage(fdm.name, mask, wcs, title)
204  fdm.flush()
205  fdm.seek(0, 0)
206  self._fireflyMaskOnServer_fireflyMaskOnServer = _fireflyClient.upload_data(fdm, 'FITS')
207 
208  maskPlaneDict = mask.getMaskPlaneDict()
209  for k, v in maskPlaneDict.items():
210  self._maskDict_maskDict[k] = v
211  self._maskPlaneColors_maskPlaneColors[k] = self.display.getMaskPlaneColor(k)
212  usedPlanes = int(afwMath.makeStatistics(mask, afwMath.SUM).getValue())
213  for k in self._maskDict_maskDict:
214  if (((1 << self._maskDict_maskDict[k]) & usedPlanes) and
215  (k in self._maskPlaneColors_maskPlaneColors) and
216  (self._maskPlaneColors_maskPlaneColors[k] is not None) and
217  (self._maskPlaneColors_maskPlaneColors[k].lower() != 'ignore')):
218  _fireflyClient.add_mask(bit_number=self._maskDict_maskDict[k],
219  image_number=0,
220  plot_id=str(self.display.frame),
221  mask_id=k,
222  title=k + ' - bit %d'%self._maskDict_maskDict[k],
223  color=self._maskPlaneColors_maskPlaneColors[k],
224  file_on_server=self._fireflyMaskOnServer_fireflyMaskOnServer)
225  if k in self._maskTransparencies_maskTransparencies:
226  self._setMaskTransparency_setMaskTransparency(self._maskTransparencies_maskTransparencies[k], k)
227  self._maskIds_maskIds.append(k)
228 
229  def _remove_masks(self):
230  """Remove mask layers"""
231  for k in self._maskIds_maskIds:
232  _fireflyClient.remove_mask(plot_id=str(self.display.frame), mask_id=k)
233  self._maskIds_maskIds = []
234 
235  def _buffer(self, enable=True):
236  """!Enable or disable buffering of writes to the display
237  param enable True or False, as appropriate
238  """
239  self._isBuffered_isBuffered = enable
240 
241  def _flush(self):
242  """!Flush any I/O buffers
243  """
244  if not self._regions_regions:
245  return
246 
247  if self.verbose:
248  print("Flushing %d regions" % len(self._regions_regions))
249  print(self._regions_regions)
250 
251  self._regionLayerId_regionLayerId = self._getRegionLayerId_getRegionLayerId()
252  _fireflyClient.add_region_data(region_data=self._regions_regions, plot_id=str(self.display.frame),
253  region_layer_id=self._regionLayerId_regionLayerId)
254  self._regions_regions = []
255 
256  def _uploadTextData(self, regions):
257  self._regions_regions += regions
258 
259  if not self._isBuffered_isBuffered:
260  self._flush_flush()
261 
262  def _close(self):
263  """Called when the device is closed"""
264  if self.verbose:
265  print("Closing firefly device %s" % (self.display.frame if self.display else "[None]"))
266  if _fireflyClient is not None:
267  _fireflyClient.disconnect()
268  _fireflyClient.session.close()
269 
270  def _dot(self, symb, c, r, size, ctype, fontFamily="helvetica", textAngle=None):
271  """Draw a symbol onto the specified DS9 frame at (col,row) = (c,r) [0-based coordinates]
272  Possible values are:
273  + Draw a +
274  x Draw an x
275  * Draw a *
276  o Draw a circle
277  @:Mxx,Mxy,Myy Draw an ellipse with moments (Mxx, Mxy, Myy) (argument size is ignored)
278  An object derived from afwGeom.ellipses.BaseCore Draw the ellipse (argument size is ignored)
279  Any other value is interpreted as a string to be drawn. Strings obey the fontFamily (which may be extended
280  with other characteristics, e.g. "times bold italic". Text will be drawn rotated by textAngle (textAngle
281  is ignored otherwise).
282 
283  N.b. objects derived from BaseCore include Axes and Quadrupole.
284  """
285  self._uploadTextData_uploadTextData(ds9Regions.dot(symb, c, r, size, ctype, fontFamily, textAngle))
286 
287  def _drawLines(self, points, ctype):
288  """Connect the points, a list of (col,row)
289  Ctype is the name of a colour (e.g. 'red')"""
290 
291  self._uploadTextData_uploadTextData(ds9Regions.drawLines(points, ctype))
292 
293  def _erase(self):
294  """Erase all overlays on the image"""
295  if self.verbose:
296  print('region layer id is {}'.format(self._regionLayerId_regionLayerId))
297  if self._regionLayerId_regionLayerId:
298  _fireflyClient.delete_region_layer(self._regionLayerId_regionLayerId, plot_id=str(self.display.frame))
299 
300  def _setCallback(self, what, func):
301  if func != interface.noop_callback:
302  try:
303  status = _fireflyClient.add_extension('POINT' if False else 'AREA_SELECT', title=what,
304  plot_id=str(self.display.frame),
305  extension_id=what)
306  if not status['success']:
307  pass
308  except Exception as e:
309  raise RuntimeError("Cannot set callback. Browser must be (re)opened " +
310  "to %s%s : %s" %
311  (_fireflyClient.url_bw,
312  _fireflyClient.channel, e))
313 
314  def _getEvent(self):
315  """Return an event generated by a keypress or mouse click
316  """
317  ev = interface.Event("q")
318 
319  if self.verbose:
320  print("virtual[%s]._getEvent() -> %s" % (self.display.frame, ev))
321 
322  return ev
323  #
324  # Set gray scale
325  #
326 
327  def _scale(self, algorithm, min, max, unit=None, *args, **kwargs):
328  """Scale the image stretch and limits
329 
330  Parameters:
331  -----------
332  algorithm : `str`
333  stretch algorithm, e.g. 'linear', 'log', 'loglog', 'equal', 'squared',
334  'sqrt', 'asinh', powerlaw_gamma'
335  min : `float` or `str`
336  lower limit, or 'minmax' for full range, or 'zscale'
337  max : `float` or `str`
338  upper limit; overrriden if min is 'minmax' or 'zscale'
339  unit : `str`
340  unit for min and max. 'percent', 'absolute', 'sigma'.
341  if not specified, min and max are presumed to be in 'absolute' units.
342 
343  *args, **kwargs : additional position and keyword arguments.
344  The options are shown below:
345 
346  **Q** : `float`, optional
347  The asinh softening parameter for asinh stretch.
348  Use Q=0 for linear stretch, increase Q to make brighter features visible.
349  When not specified or None, Q is calculated by Firefly to use full color range.
350  **gamma**
351  The gamma value for power law gamma stretch (default 2.0)
352  **zscale_contrast** : `int`, optional
353  Contrast parameter in percent for zscale algorithm (default 25)
354  **zscale_samples** : `int`, optional
355  Number of samples for zscale algorithm (default 600)
356  **zscale_samples_perline** : `int`, optional
357  Number of samples per line for zscale algorithm (default 120)
358  """
359  stretch_algorithms = ('linear', 'log', 'loglog', 'equal', 'squared', 'sqrt',
360  'asinh', 'powerlaw_gamma')
361  interval_methods = ('percent', 'maxmin', 'absolute', 'zscale', 'sigma')
362  #
363  #
364  # Normalise algorithm's case
365  #
366  if algorithm:
367  algorithm = dict((a.lower(), a) for a in stretch_algorithms).get(algorithm.lower(), algorithm)
368 
369  if algorithm not in stretch_algorithms:
370  raise FireflyError('Algorithm %s is invalid; please choose one of "%s"' %
371  (algorithm, '", "'.join(stretch_algorithms)))
372  self._stretchAlgorithm_stretchAlgorithm = algorithm
373  else:
374  algorithm = 'linear'
375 
376  # Translate parameters for asinh and powerlaw_gamma stretches
377  if 'Q' in kwargs:
378  kwargs['asinh_q_value'] = kwargs['Q']
379  del kwargs['Q']
380 
381  if 'gamma' in kwargs:
382  kwargs['gamma_value'] = kwargs['gamma']
383  del kwargs['gamma']
384 
385  if min == 'minmax':
386  interval_type = 'percent'
387  unit = 'percent'
388  min, max = 0, 100
389  elif min == 'zscale':
390  interval_type = 'zscale'
391  else:
392  interval_type = None
393 
394  if not unit:
395  unit = 'absolute'
396 
397  units = ('percent', 'absolute', 'sigma')
398  if unit not in units:
399  raise FireflyError('Unit %s is invalid; please choose one of "%s"' % (unit, '", "'.join(units)))
400 
401  if unit == 'sigma':
402  interval_type = 'sigma'
403  elif unit == 'absolute' and interval_type is None:
404  interval_type = 'absolute'
405  elif unit == 'percent':
406  interval_type = 'percent'
407 
408  self._stretchMin_stretchMin = min
409  self._stretchMax_stretchMax = max
410  self._stretchUnit_stretchUnit = unit
411 
412  if interval_type not in interval_methods:
413  raise FireflyError('Interval method %s is invalid' % interval_type)
414 
415  rval = {}
416  if interval_type != 'zscale':
417  rval = _fireflyClient.set_stretch(str(self.display.frame), stype=interval_type,
418  algorithm=algorithm, lower_value=min,
419  upper_value=max, **kwargs)
420  else:
421  if 'zscale_contrast' not in kwargs:
422  kwargs['zscale_contrast'] = 25
423  if 'zscale_samples' not in kwargs:
424  kwargs['zscale_samples'] = 600
425  if 'zscale_samples_perline' not in kwargs:
426  kwargs['zscale_samples_perline'] = 120
427  rval = _fireflyClient.set_stretch(str(self.display.frame), stype='zscale',
428  algorithm=algorithm, **kwargs)
429 
430  if 'rv_string' in rval:
431  self._lastStretch_lastStretch = rval['rv_string']
432 
433  def _setMaskTransparency(self, transparency, maskName):
434  """Specify mask transparency (percent); or None to not set it when loading masks"""
435  if maskName is not None:
436  masklist = [maskName]
437  else:
438  masklist = set(self._maskIds_maskIds + list(self.display._defaultMaskPlaneColor.keys()))
439  for k in masklist:
440  self._maskTransparencies_maskTransparencies[k] = transparency
441  _fireflyClient.dispatch(action_type='ImagePlotCntlr.overlayPlotChangeAttributes',
442  payload={'plotId': str(self.display.frame),
443  'imageOverlayId': k,
444  'attributes': {'opacity': 1.0 - transparency/100.},
445  'doReplot': False})
446 
447  def _getMaskTransparency(self, maskName):
448  """Return the current mask's transparency"""
449  transparency = None
450  if maskName in self._maskTransparencies_maskTransparencies:
451  transparency = self._maskTransparencies_maskTransparencies[maskName]
452  return transparency
453 
454  def _setMaskPlaneColor(self, maskName, color):
455  """Specify mask color """
456  _fireflyClient.remove_mask(plot_id=str(self.display.frame),
457  mask_id=maskName)
458  self._maskPlaneColors_maskPlaneColors[maskName] = color
459  if (color.lower() != 'ignore'):
460  _fireflyClient.add_mask(bit_number=self._maskDict_maskDict[maskName],
461  image_number=1,
462  plot_id=str(self.display.frame),
463  mask_id=maskName,
464  color=self.display.getMaskPlaneColor(maskName),
465  file_on_server=self._fireflyFitsID_fireflyFitsID)
466 
467  def _show(self):
468  """Show the requested window"""
469  if self._client_client.render_tree_id is not None:
470  # we are using Jupyterlab
471  self._client_client.dispatch(self._client_client.ACTION_DICT['StartLabWindow'],
472  {})
473  else:
474  localbrowser, url = _fireflyClient.launch_browser(verbose=self.verbose)
475  if not localbrowser and not self.verbose:
476  _fireflyClient.display_url()
477 
478  #
479  # Zoom and Pan
480  #
481 
482  def _zoom(self, zoomfac):
483  """Zoom display by specified amount
484 
485  Parameters:
486  -----------
487  zoomfac: `float`
488  zoom level in screen pixels per image pixel
489  """
490  self._lastZoom_lastZoom = zoomfac
491  _fireflyClient.set_zoom(plot_id=str(self.display.frame), factor=zoomfac)
492 
493  def _pan(self, colc, rowc):
494  """Pan to specified pixel coordinates
495 
496  Parameters:
497  -----------
498  colc, rowc : `float`
499  column and row in units of pixels (zero-based convention,
500  with the xy0 already subtracted off)
501  """
502  self._lastPan_lastPan = [colc+0.5, rowc+0.5] # saved for future use in _mtv
503  # Firefly's internal convention is first pixel is (0.5, 0.5)
504  _fireflyClient.set_pan(plot_id=str(self.display.frame), x=colc, y=rowc)
505 
506  # Extensions to the API that are specific to using the Firefly backend
507 
508  def getClient(self):
509  """Get the instance of FireflyClient for this display
510 
511  Returns:
512  --------
513  `firefly_client.FireflyClient`
514  Instance of FireflyClient used by this display
515  """
516  return self._client_client
517 
518  def clearViewer(self):
519  """Reinitialize the viewer
520  """
521  self._client_client.reinit_viewer()
522 
523  def resetLayout(self):
524  """Reset the layout of the Firefly Slate browser
525 
526  Clears the display and adds Slate cells to display image in upper left,
527  plot area in upper right, and plots stretch across the bottom
528  """
529  self.clearViewerclearViewer()
530  try:
531  tables_cell_id = 'tables-' + str(_fireflyClient.render_tree_id)
532  except AttributeError:
533  tables_cell_id = 'tables'
534  self._client_client.add_cell(row=2, col=0, width=4, height=2, element_type='tables',
535  cell_id=tables_cell_id)
536  try:
537  image_cell_id = ('image-' + str(_fireflyClient.render_tree_id) + '-' +
538  str(self.frame))
539  except AttributeError:
540  image_cell_id = 'image-' + str(self.frame)
541  self._client_client.add_cell(row=0, col=0, width=2, height=3, element_type='images',
542  cell_id=image_cell_id)
543  try:
544  plots_cell_id = 'plots-' + str(_fireflyClient.render_tree_id)
545  except AttributeError:
546  plots_cell_id = 'plots'
547  self._client_client.add_cell(row=0, col=2, width=2, height=3, element_type='xyPlots',
548  cell_id=plots_cell_id)
549 
550  def overlayFootprints(self, catalog, color='rgba(74,144,226,0.60)',
551  highlightColor='cyan', selectColor='orange',
552  style='fill', layerString='detection footprints ',
553  titleString='catalog footprints '):
554  """Overlay outlines of footprints from a catalog
555 
556  Overlay outlines of LSST footprints from the input catalog. The colors
557  and style can be specified as parameters, and the base color and style
558  can be changed in the Firefly browser user interface.
559 
560  Parameters:
561  -----------
562  catalog : `lsst.afw.table.SourceCatalog`
563  Source catalog from which to display footprints.
564  color : `str`
565  Color for footprints overlay. Colors can be specified as a name
566  like 'cyan' or afwDisplay.RED; as an rgb value such as
567  'rgb(80,100,220)'; or as rgb plus alpha (transparency) such
568  as 'rgba('74,144,226,0.60)'.
569  highlightColor : `str`
570  Color for highlighted footprints
571  selectColor : `str`
572  Color for selected footprints
573  style : {'fill', 'outline'}
574  Style of footprints display, filled or outline
575  insertColumn : `int`
576  Column at which to insert the "family_id" and "category" columns
577  layerString: `str`
578  Name of footprints layer string, to concatenate with the frame
579  Re-using the layer_string will overwrite the previous table and
580  footprints
581  titleString: `str`
582  Title of catalog, to concatenate with the frame
583  """
584  footprintTable = createFootprintsTable(catalog)
585  with BytesIO() as fd:
586  footprintTable.to_xml(fd)
587  tableval = self._client_client.upload_data(fd, 'UNKNOWN')
588  self._client_client.overlay_footprints(footprint_file=tableval,
589  title=titleString + str(self.display.frame),
590  footprint_layer_id=layerString + str(self.display.frame),
591  plot_id=str(self.display.frame),
592  color=color,
593  highlightColor=highlightColor,
594  selectColor=selectColor,
595  style=style)
def __init__(self, display, verbose=False, url=None, name=None, *args, **kwargs)
Definition: firefly.py:76
def _setMaskTransparency(self, transparency, maskName)
Definition: firefly.py:433
def overlayFootprints(self, catalog, color='rgba(74, 144, 226, 0.60)', highlightColor='cyan', selectColor='orange', style='fill', layerString='detection footprints ', titleString='catalog footprints ')
Definition: firefly.py:553
daf::base::PropertyList * list
Definition: fits.cc:913
daf::base::PropertySet * set
Definition: fits.cc:912
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
Definition: functional.cc:33
def getMaskPlaneColor(name, frame=None)
Definition: ds9.py:76
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())
Handle a watered-down front-end to the constructor (no variance)
Definition: Statistics.h:359
def createFootprintsTable(catalog, xy0=None, insertColumn=4)
Definition: footprints.py:62
Definition: Log.h:717
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:174