23 from __future__
import absolute_import, division, print_function
24 from past.builtins
import long
26 from io
import BytesIO
27 from socket
import gaierror
37 from .footprints
import createFootprintsTable
42 except ImportError
as e:
43 raise RuntimeError(
"Cannot import firefly_client: %s" % (e))
44 from ws4py.client
import HandshakeError
50 Exception.__init__(self, str)
54 """Return the version of firefly_client in use, as a string""" 55 return(firefly_client.__version__)
59 """Device to talk to a firefly display""" 62 def __handleCallbacks(event):
63 if 'type' in event[
'data']:
64 if event[
'data'][
'type'] ==
'AREA_SELECT':
65 lsst.log.debug(
'*************area select')
66 pParams = {
'URL':
'http://web.ipac.caltech.edu/staff/roby/demo/wise-m51-band2.fits',
70 _fireflyClient.show_fits(fileOnServer=
None, plot_id=plot_id, additionalParams=pParams)
72 lsst.log.debug(
"Callback event info: {}".
format(event))
74 data = dict((_.split(
'=')
for _
in event.get(
'data', {}).split(
'&')))
75 if data.get(
'type') ==
"POINT":
76 lsst.log.debug(
"Event Received: %s" % data.get(
'id'))
78 def __init__(self, display, verbose=False, url=None,
79 name=None, *args, **kwargs):
80 virtualDevice.DisplayImpl.__init__(self, display, verbose)
83 print(
"Opening firefly device %s" % (self.display.frame
if self.display
else "[None]"))
86 if not _fireflyClient:
89 html_file = kwargs.get(
'html_file',
90 os.environ.get(
'FIREFLY_HTML',
'slate.html'))
92 if ((
'fireflyLabExtension' in os.environ)
and 93 (
'fireflyURLLab' in os.environ)):
94 url = os.environ[
'fireflyURLLab']
95 start_tab = kwargs.get(
'start_tab',
True)
96 start_browser_tab = kwargs.get(
'start_browser_tab',
False)
97 if (name
is None)
and (
'fireflyChannelLab' in os.environ):
98 name = os.environ[
'fireflyChannelLab']
99 elif 'FIREFLY_URL' in os.environ:
100 url = os.environ[
'FIREFLY_URL']
102 raise RuntimeError(
'Cannot determine url from environment; you must pass url')
106 print(
'Starting Jupyterlab client')
107 _fireflyClient = firefly_client.FireflyClient.make_lab_client(
108 start_tab=
True, start_browser_tab=start_browser_tab,
109 html_file=kwargs.get(
'html_file'), verbose=verbose)
112 print(
'Starting vanilla client')
113 _fireflyClient = firefly_client.FireflyClient.make_client(
114 url=url, html_file=html_file, launch_browser=
True,
115 channel_override=name, verbose=verbose)
116 except (HandshakeError, gaierror)
as e:
117 raise RuntimeError(
"Unable to connect to %s: %s" % (url
or '', e))
121 except Exception
as e:
122 raise RuntimeError(
"Cannot add listener. Browser must be connected" +
124 (_fireflyClient.get_firefly_url(), e))
132 self.
_channel = _fireflyClient.channel
133 self.
_url = _fireflyClient.get_firefly_url()
142 def _getRegionLayerId(self):
143 return "lsstRegions%s" % self.display.frame
if self.display
else "None" 145 def _clearImage(self):
146 """Delete the current image in the Firefly viewer 148 self.
_client.dispatch(action_type=
'ImagePlotCntlr.deletePlotView',
149 payload=dict(plotId=
str(self.display.frame)))
151 def _mtv(self, image, mask=None, wcs=None, title=""):
152 """Display an Image and/or Mask on a Firefly display 155 title =
str(self.display.frame)
158 print(
'displaying image')
161 with tempfile.NamedTemporaryFile()
as fd:
162 displayLib.writeFitsImage(fd.name, image, wcs, title)
168 viewer_id = (
'image-' +
str(_fireflyClient.render_tree_id) +
'-' +
170 except AttributeError:
171 viewer_id =
'image-' +
str(self.frame)
172 extraParams = dict(Title=title,
174 PredefinedOverlayIds=
' ',
180 extraParams[
'InitZoomLevel'] = self.
_lastZoom 181 extraParams[
'ZoomType'] =
'LEVEL' 183 extraParams[
'InitialCenterPosition'] =
'{0:.3f};{1:.3f};PIXEL'.
format(
188 ret = _fireflyClient.show_fits(self.
_fireflyFitsID, plot_id=
str(self.display.frame),
191 if not ret[
"success"]:
192 raise RuntimeError(
"Display of image failed")
196 print(
'displaying mask')
197 with tempfile.NamedTemporaryFile()
as fdm:
198 displayLib.writeFitsImage(fdm.name, mask, wcs, title)
203 maskPlaneDict = mask.getMaskPlaneDict()
204 for k, v
in maskPlaneDict.items():
209 if (((1 << self.
_maskDict[k]) & usedPlanes)
and 213 _fireflyClient.add_mask(bit_number=self.
_maskDict[k],
215 plot_id=
str(self.display.frame),
224 def _remove_masks(self):
225 """Remove mask layers""" 227 _fireflyClient.remove_mask(plot_id=
str(self.display.frame), mask_id=k)
230 def _buffer(self, enable=True):
231 """!Enable or disable buffering of writes to the display 232 param enable True or False, as appropriate 237 """!Flush any I/O buffers 243 print(
"Flushing %d regions" % len(self.
_regions))
247 _fireflyClient.add_region_data(region_data=self.
_regions, plot_id=
str(self.display.frame),
251 def _uploadTextData(self, regions):
258 """Called when the device is closed""" 260 print(
"Closing firefly device %s" % (self.display.frame
if self.display
else "[None]"))
261 if _fireflyClient
is not None:
262 _fireflyClient.disconnect()
263 _fireflyClient.session.close()
265 def _dot(self, symb, c, r, size, ctype, fontFamily="helvetica", textAngle=None):
266 """Draw a symbol onto the specified DS9 frame at (col,row) = (c,r) [0-based coordinates] 272 @:Mxx,Mxy,Myy Draw an ellipse with moments (Mxx, Mxy, Myy) (argument size is ignored) 273 An object derived from afwGeom.ellipses.BaseCore Draw the ellipse (argument size is ignored) 274 Any other value is interpreted as a string to be drawn. Strings obey the fontFamily (which may be extended 275 with other characteristics, e.g. "times bold italic". Text will be drawn rotated by textAngle (textAngle 276 is ignored otherwise). 278 N.b. objects derived from BaseCore include Axes and Quadrupole. 280 self.
_uploadTextData(ds9Regions.dot(symb, c, r, size, ctype, fontFamily, textAngle))
282 def _drawLines(self, points, ctype):
283 """Connect the points, a list of (col,row) 284 Ctype is the name of a colour (e.g. 'red')""" 289 """Erase all overlays on the image""" 293 _fireflyClient.delete_region_layer(self.
_regionLayerId, plot_id=
str(self.display.frame))
295 def _setCallback(self, what, func):
296 if func != interface.noop_callback:
298 status = _fireflyClient.add_extension(
'POINT' if False else 'AREA_SELECT', title=what,
299 plot_id=
str(self.display.frame),
301 if not status[
'success']:
303 except Exception
as e:
304 raise RuntimeError(
"Cannot set callback. Browser must be (re)opened " +
306 (_fireflyClient.url_bw,
307 _fireflyClient.channel, e))
310 """Return an event generated by a keypress or mouse click 312 ev = interface.Event(
"q")
315 print(
"virtual[%s]._getEvent() -> %s" % (self.display.frame, ev))
322 def _scale(self, algorithm, min, max, unit=None, *args, **kwargs):
323 """Scale the image stretch and limits 328 stretch algorithm, e.g. 'linear', 'log', 'loglog', 'equal', 'squared', 329 'sqrt', 'asinh', powerlaw_gamma' 330 min : `float` or `str` 331 lower limit, or 'minmax' for full range, or 'zscale' 332 max : `float` or `str` 333 upper limit; overrriden if min is 'minmax' or 'zscale' 335 unit for min and max. 'percent', 'absolute', 'sigma'. 336 if not specified, min and max are presumed to be in 'absolute' units. 338 *args, **kwargs : additional position and keyword arguments. 339 The options are shown below: 341 **Q** : `float`, optional 342 The asinh softening parameter for asinh stretch. 343 Use Q=0 for linear stretch, increase Q to make brighter features visible. 344 When not specified or None, Q is calculated by Firefly to use full color range. 346 The gamma value for power law gamma stretch (default 2.0) 347 **zscale_contrast** : `int`, optional 348 Contrast parameter in percent for zscale algorithm (default 25) 349 **zscale_samples** : `int`, optional 350 Number of samples for zscale algorithm (default 600) 351 **zscale_samples_perline** : `int`, optional 352 Number of samples per line for zscale algorithm (default 120) 354 stretch_algorithms = (
'linear',
'log',
'loglog',
'equal',
'squared',
'sqrt',
355 'asinh',
'powerlaw_gamma')
356 interval_methods = (
'percent',
'maxmin',
'absolute',
'zscale',
'sigma')
362 algorithm = dict((a.lower(), a)
for a
in stretch_algorithms).get(algorithm.lower(), algorithm)
364 if algorithm
not in stretch_algorithms:
365 raise FireflyError(
'Algorithm %s is invalid; please choose one of "%s"' %
366 (algorithm,
'", "'.join(stretch_algorithms)))
373 kwargs[
'asinh_q_value'] = kwargs[
'Q']
376 if 'gamma' in kwargs:
377 kwargs[
'gamma_value'] = kwargs[
'gamma']
381 interval_type =
'percent' 384 elif min ==
'zscale':
385 interval_type =
'zscale' 392 units = (
'percent',
'absolute',
'sigma')
393 if unit
not in units:
394 raise FireflyError(
'Unit %s is invalid; please choose one of "%s"' % (unit,
'", "'.join(units)))
397 interval_type =
'sigma' 398 elif unit ==
'absolute' and interval_type
is None:
399 interval_type =
'absolute' 400 elif unit ==
'percent':
401 interval_type =
'percent' 407 if interval_type
not in interval_methods:
408 raise FireflyError(
'Interval method %s is invalid' % interval_type)
411 if interval_type !=
'zscale':
412 rval = _fireflyClient.set_stretch(
str(self.display.frame), stype=interval_type,
413 algorithm=algorithm, lower_value=min,
414 upper_value=max, **kwargs)
416 if 'zscale_contrast' not in kwargs:
417 kwargs[
'zscale_contrast'] = 25
418 if 'zscale_samples' not in kwargs:
419 kwargs[
'zscale_samples'] = 600
420 if 'zscale_samples_perline' not in kwargs:
421 kwargs[
'zscale_samples_perline'] = 120
422 rval = _fireflyClient.set_stretch(
str(self.display.frame), stype=
'zscale',
423 algorithm=algorithm, **kwargs)
425 if 'rv_string' in rval:
428 def _setMaskTransparency(self, transparency, maskName):
429 """Specify mask transparency (percent); or None to not set it when loading masks""" 430 if maskName
is not None:
431 masklist = [maskName]
433 masklist =
set(self.
_maskIds +
list(self.display._defaultMaskPlaneColor.keys()))
436 _fireflyClient.dispatch(action_type=
'ImagePlotCntlr.overlayPlotChangeAttributes',
437 payload={
'plotId':
str(self.display.frame),
439 'attributes': {
'opacity': 1.0 - transparency/100.},
442 def _getMaskTransparency(self, maskName):
443 """Return the current mask's transparency""" 449 def _setMaskPlaneColor(self, maskName, color):
450 """Specify mask color """ 451 _fireflyClient.remove_mask(plot_id=
str(self.display.frame),
454 if (color.lower() !=
'ignore'):
455 _fireflyClient.add_mask(bit_number=self.
_maskDict[maskName],
457 plot_id=
str(self.display.frame),
463 """Show the requested window""" 464 if self.
_client.render_tree_id
is not None:
469 localbrowser, url = _fireflyClient.launch_browser(verbose=self.verbose)
470 if not localbrowser
and not self.verbose:
471 _fireflyClient.display_url()
477 def _zoom(self, zoomfac):
478 """Zoom display by specified amount 483 zoom level in screen pixels per image pixel 486 _fireflyClient.set_zoom(plot_id=
str(self.display.frame), factor=zoomfac)
488 def _pan(self, colc, rowc):
489 """Pan to specified pixel coordinates 494 column and row in units of pixels (zero-based convention, 495 with the xy0 already subtracted off) 497 self.
_lastPan = [colc+0.5, rowc+0.5]
499 _fireflyClient.set_pan(plot_id=
str(self.display.frame), x=colc, y=rowc)
504 """Get the instance of FireflyClient for this display 508 `firefly_client.FireflyClient` 509 Instance of FireflyClient used by this display 514 """Reinitialize the viewer 519 """Reset the layout of the Firefly Slate browser 521 Clears the display and adds Slate cells to display image in upper left, 522 plot area in upper right, and plots stretch across the bottom 526 tables_cell_id =
'tables-' +
str(_fireflyClient.render_tree_id)
527 except AttributeError:
528 tables_cell_id =
'tables' 529 self.
_client.add_cell(row=2, col=0, width=4, height=2, element_type=
'tables',
530 cell_id=tables_cell_id)
532 image_cell_id = (
'image-' +
str(_fireflyClient.render_tree_id) +
'-' +
534 except AttributeError:
535 image_cell_id =
'image-' +
str(self.frame)
536 self.
_client.add_cell(row=0, col=0, width=2, height=3, element_type=
'images',
537 cell_id=image_cell_id)
539 plots_cell_id =
'plots-' +
str(_fireflyClient.render_tree_id)
540 except AttributeError:
541 plots_cell_id =
'plots' 542 self.
_client.add_cell(row=0, col=2, width=2, height=3, element_type=
'xyPlots',
543 cell_id=plots_cell_id)
546 highlightColor='cyan', selectColor=
'orange',
547 style=
'fill', layerString=
'detection footprints ',
548 titleString=
'catalog footprints '):
549 """Overlay outlines of footprints from a catalog 551 Overlay outlines of LSST footprints from the input catalog. The colors 552 and style can be specified as parameters, and the base color and style 553 can be changed in the Firefly browser user interface. 557 catalog : `lsst.afw.table.SourceCatalog` 558 Source catalog from which to display footprints. 560 Color for footprints overlay. Colors can be specified as a name 561 like 'cyan' or afwDisplay.RED; as an rgb value such as 562 'rgb(80,100,220)'; or as rgb plus alpha (transparency) such 563 as 'rgba('74,144,226,0.60)'. 564 highlightColor : `str` 565 Color for highlighted footprints 567 Color for selected footprints 568 style : {'fill', 'outline'} 569 Style of footprints display, filled or outline 571 Column at which to insert the "family_id" and "category" columns 573 Name of footprints layer string, to concatenate with the frame 574 Re-using the layer_string will overwrite the previous table and 577 Title of catalog, to concatenate with the frame 580 with BytesIO()
as fd:
581 footprintTable.to_xml(fd)
582 tableval = self.
_client.upload_data(fd,
'UNKNOWN')
583 self.
_client.overlay_footprints(footprint_file=tableval,
584 title=titleString +
str(self.display.frame),
585 footprint_layer_id=layerString +
str(self.display.frame),
586 plot_id=
str(self.display.frame),
588 highlightColor=highlightColor,
589 selectColor=selectColor,
def _setMaskTransparency(self, transparency, maskName)
def __handleCallbacks(event)
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
daf::base::PropertySet * set
Statistics makeStatistics(lsst::afw::math::MaskedVector< EntryT > const &mv, std::vector< WeightPixel > const &vweights, int const flags, StatisticsControl const &sctrl=StatisticsControl())
The makeStatistics() overload to handle lsst::afw::math::MaskedVector<>
def _flush(self)
Flush any I/O buffers.
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
def overlayFootprints(self, catalog, color='rgba(74, 144, 226, 0.60)', highlightColor='cyan', selectColor='orange', style='fill', layerString='detection footprints ', titleString='catalog footprints ')
def _uploadTextData(self, regions)
def getMaskPlaneColor(name, frame=None)
def _getRegionLayerId(self)
def __init__(self, display, verbose=False, url=None, name=None, args, kwargs)
daf::base::PropertyList * list