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')
104 token = kwargs.get(
'token',
105 os.environ.get(
'ACCESS_TOKEN',
None))
110 print(
'Starting Jupyterlab client')
111 _fireflyClient = firefly_client.FireflyClient.make_lab_client(
112 start_tab=
True, start_browser_tab=start_browser_tab,
113 html_file=kwargs.get(
'html_file'), verbose=verbose,
118 print(
'Starting vanilla client')
119 _fireflyClient = firefly_client.FireflyClient.make_client(
120 url=url, html_file=html_file, launch_browser=
True,
121 channel_override=name, verbose=verbose,
124 except (HandshakeError, gaierror)
as e:
125 raise RuntimeError(
"Unable to connect to %s: %s" % (url
or '', e))
129 except Exception
as e:
130 raise RuntimeError(
"Cannot add listener. Browser must be connected" +
132 (_fireflyClient.get_firefly_url(), e))
140 self.
_channel = _fireflyClient.channel
141 self.
_url = _fireflyClient.get_firefly_url()
150 def _getRegionLayerId(self):
151 return "lsstRegions%s" % self.display.frame
if self.display
else "None" 153 def _clearImage(self):
154 """Delete the current image in the Firefly viewer 156 self.
_client.dispatch(action_type=
'ImagePlotCntlr.deletePlotView',
157 payload=dict(plotId=str(self.display.frame)))
159 def _mtv(self, image, mask=None, wcs=None, title=""):
160 """Display an Image and/or Mask on a Firefly display 163 title = str(self.display.frame)
166 print(
'displaying image')
169 with tempfile.NamedTemporaryFile()
as fd:
170 displayLib.writeFitsImage(fd.name, image, wcs, title)
176 viewer_id = (
'image-' + str(_fireflyClient.render_tree_id) +
'-' +
178 except AttributeError:
179 viewer_id =
'image-' + str(self.frame)
180 extraParams = dict(Title=title,
182 PredefinedOverlayIds=
' ',
188 extraParams[
'InitZoomLevel'] = self.
_lastZoom 189 extraParams[
'ZoomType'] =
'LEVEL' 191 extraParams[
'InitialCenterPosition'] =
'{0:.3f};{1:.3f};PIXEL'.
format(
196 ret = _fireflyClient.show_fits(self.
_fireflyFitsID, plot_id=str(self.display.frame),
199 if not ret[
"success"]:
200 raise RuntimeError(
"Display of image failed")
204 print(
'displaying mask')
205 with tempfile.NamedTemporaryFile()
as fdm:
206 displayLib.writeFitsImage(fdm.name, mask, wcs, title)
211 maskPlaneDict = mask.getMaskPlaneDict()
212 for k, v
in maskPlaneDict.items():
217 if (((1 << self.
_maskDict[k]) & usedPlanes)
and 221 _fireflyClient.add_mask(bit_number=self.
_maskDict[k],
223 plot_id=str(self.display.frame),
232 def _remove_masks(self):
233 """Remove mask layers""" 235 _fireflyClient.remove_mask(plot_id=str(self.display.frame), mask_id=k)
238 def _buffer(self, enable=True):
239 """!Enable or disable buffering of writes to the display 240 param enable True or False, as appropriate 245 """!Flush any I/O buffers 251 print(
"Flushing %d regions" % len(self.
_regions))
255 _fireflyClient.add_region_data(region_data=self.
_regions, plot_id=str(self.display.frame),
259 def _uploadTextData(self, regions):
266 """Called when the device is closed""" 268 print(
"Closing firefly device %s" % (self.display.frame
if self.display
else "[None]"))
269 if _fireflyClient
is not None:
270 _fireflyClient.disconnect()
271 _fireflyClient.session.close()
273 def _dot(self, symb, c, r, size, ctype, fontFamily="helvetica", textAngle=None):
274 """Draw a symbol onto the specified DS9 frame at (col,row) = (c,r) [0-based coordinates] 280 @:Mxx,Mxy,Myy Draw an ellipse with moments (Mxx, Mxy, Myy) (argument size is ignored) 281 An object derived from afwGeom.ellipses.BaseCore Draw the ellipse (argument size is ignored) 282 Any other value is interpreted as a string to be drawn. Strings obey the fontFamily (which may be extended 283 with other characteristics, e.g. "times bold italic". Text will be drawn rotated by textAngle (textAngle 284 is ignored otherwise). 286 N.b. objects derived from BaseCore include Axes and Quadrupole. 288 self.
_uploadTextData(ds9Regions.dot(symb, c, r, size, ctype, fontFamily, textAngle))
290 def _drawLines(self, points, ctype):
291 """Connect the points, a list of (col,row) 292 Ctype is the name of a colour (e.g. 'red')""" 297 """Erase all overlays on the image""" 301 _fireflyClient.delete_region_layer(self.
_regionLayerId, plot_id=str(self.display.frame))
303 def _setCallback(self, what, func):
304 if func != interface.noop_callback:
306 status = _fireflyClient.add_extension(
'POINT' if False else 'AREA_SELECT', title=what,
307 plot_id=str(self.display.frame),
309 if not status[
'success']:
311 except Exception
as e:
312 raise RuntimeError(
"Cannot set callback. Browser must be (re)opened " +
314 (_fireflyClient.url_bw,
315 _fireflyClient.channel, e))
318 """Return an event generated by a keypress or mouse click 320 ev = interface.Event(
"q")
323 print(
"virtual[%s]._getEvent() -> %s" % (self.display.frame, ev))
330 def _scale(self, algorithm, min, max, unit=None, *args, **kwargs):
331 """Scale the image stretch and limits 336 stretch algorithm, e.g. 'linear', 'log', 'loglog', 'equal', 'squared', 337 'sqrt', 'asinh', powerlaw_gamma' 338 min : `float` or `str` 339 lower limit, or 'minmax' for full range, or 'zscale' 340 max : `float` or `str` 341 upper limit; overrriden if min is 'minmax' or 'zscale' 343 unit for min and max. 'percent', 'absolute', 'sigma'. 344 if not specified, min and max are presumed to be in 'absolute' units. 346 *args, **kwargs : additional position and keyword arguments. 347 The options are shown below: 349 **Q** : `float`, optional 350 The asinh softening parameter for asinh stretch. 351 Use Q=0 for linear stretch, increase Q to make brighter features visible. 352 When not specified or None, Q is calculated by Firefly to use full color range. 354 The gamma value for power law gamma stretch (default 2.0) 355 **zscale_contrast** : `int`, optional 356 Contrast parameter in percent for zscale algorithm (default 25) 357 **zscale_samples** : `int`, optional 358 Number of samples for zscale algorithm (default 600) 359 **zscale_samples_perline** : `int`, optional 360 Number of samples per line for zscale algorithm (default 120) 362 stretch_algorithms = (
'linear',
'log',
'loglog',
'equal',
'squared',
'sqrt',
363 'asinh',
'powerlaw_gamma')
364 interval_methods = (
'percent',
'maxmin',
'absolute',
'zscale',
'sigma')
370 algorithm = dict((a.lower(), a)
for a
in stretch_algorithms).get(algorithm.lower(), algorithm)
372 if algorithm
not in stretch_algorithms:
373 raise FireflyError(
'Algorithm %s is invalid; please choose one of "%s"' %
374 (algorithm,
'", "'.join(stretch_algorithms)))
381 kwargs[
'asinh_q_value'] = kwargs[
'Q']
384 if 'gamma' in kwargs:
385 kwargs[
'gamma_value'] = kwargs[
'gamma']
389 interval_type =
'percent' 392 elif min ==
'zscale':
393 interval_type =
'zscale' 400 units = (
'percent',
'absolute',
'sigma')
401 if unit
not in units:
402 raise FireflyError(
'Unit %s is invalid; please choose one of "%s"' % (unit,
'", "'.join(units)))
405 interval_type =
'sigma' 406 elif unit ==
'absolute' and interval_type
is None:
407 interval_type =
'absolute' 408 elif unit ==
'percent':
409 interval_type =
'percent' 415 if interval_type
not in interval_methods:
416 raise FireflyError(
'Interval method %s is invalid' % interval_type)
419 if interval_type !=
'zscale':
420 rval = _fireflyClient.set_stretch(str(self.display.frame), stype=interval_type,
421 algorithm=algorithm, lower_value=min,
422 upper_value=max, **kwargs)
424 if 'zscale_contrast' not in kwargs:
425 kwargs[
'zscale_contrast'] = 25
426 if 'zscale_samples' not in kwargs:
427 kwargs[
'zscale_samples'] = 600
428 if 'zscale_samples_perline' not in kwargs:
429 kwargs[
'zscale_samples_perline'] = 120
430 rval = _fireflyClient.set_stretch(str(self.display.frame), stype=
'zscale',
431 algorithm=algorithm, **kwargs)
433 if 'rv_string' in rval:
436 def _setMaskTransparency(self, transparency, maskName):
437 """Specify mask transparency (percent); or None to not set it when loading masks""" 438 if maskName
is not None:
439 masklist = [maskName]
441 masklist =
set(self.
_maskIds +
list(self.display._defaultMaskPlaneColor.keys()))
444 _fireflyClient.dispatch(action_type=
'ImagePlotCntlr.overlayPlotChangeAttributes',
445 payload={
'plotId': str(self.display.frame),
447 'attributes': {
'opacity': 1.0 - transparency/100.},
450 def _getMaskTransparency(self, maskName):
451 """Return the current mask's transparency""" 457 def _setMaskPlaneColor(self, maskName, color):
458 """Specify mask color """ 459 _fireflyClient.remove_mask(plot_id=str(self.display.frame),
462 if (color.lower() !=
'ignore'):
463 _fireflyClient.add_mask(bit_number=self.
_maskDict[maskName],
465 plot_id=str(self.display.frame),
471 """Show the requested window""" 472 if self.
_client.render_tree_id
is not None:
477 localbrowser, url = _fireflyClient.launch_browser(verbose=self.verbose)
478 if not localbrowser
and not self.verbose:
479 _fireflyClient.display_url()
485 def _zoom(self, zoomfac):
486 """Zoom display by specified amount 491 zoom level in screen pixels per image pixel 494 _fireflyClient.set_zoom(plot_id=str(self.display.frame), factor=zoomfac)
496 def _pan(self, colc, rowc):
497 """Pan to specified pixel coordinates 502 column and row in units of pixels (zero-based convention, 503 with the xy0 already subtracted off) 505 self.
_lastPan = [colc+0.5, rowc+0.5]
507 _fireflyClient.set_pan(plot_id=str(self.display.frame), x=colc, y=rowc)
512 """Get the instance of FireflyClient for this display 516 `firefly_client.FireflyClient` 517 Instance of FireflyClient used by this display 522 """Reinitialize the viewer 527 """Reset the layout of the Firefly Slate browser 529 Clears the display and adds Slate cells to display image in upper left, 530 plot area in upper right, and plots stretch across the bottom 534 tables_cell_id =
'tables-' + str(_fireflyClient.render_tree_id)
535 except AttributeError:
536 tables_cell_id =
'tables' 537 self.
_client.add_cell(row=2, col=0, width=4, height=2, element_type=
'tables',
538 cell_id=tables_cell_id)
540 image_cell_id = (
'image-' + str(_fireflyClient.render_tree_id) +
'-' +
542 except AttributeError:
543 image_cell_id =
'image-' + str(self.frame)
544 self.
_client.add_cell(row=0, col=0, width=2, height=3, element_type=
'images',
545 cell_id=image_cell_id)
547 plots_cell_id =
'plots-' + str(_fireflyClient.render_tree_id)
548 except AttributeError:
549 plots_cell_id =
'plots' 550 self.
_client.add_cell(row=0, col=2, width=2, height=3, element_type=
'xyPlots',
551 cell_id=plots_cell_id)
554 highlightColor='cyan', selectColor=
'orange',
555 style=
'fill', layerString=
'detection footprints ',
556 titleString=
'catalog footprints '):
557 """Overlay outlines of footprints from a catalog 559 Overlay outlines of LSST footprints from the input catalog. The colors 560 and style can be specified as parameters, and the base color and style 561 can be changed in the Firefly browser user interface. 565 catalog : `lsst.afw.table.SourceCatalog` 566 Source catalog from which to display footprints. 568 Color for footprints overlay. Colors can be specified as a name 569 like 'cyan' or afwDisplay.RED; as an rgb value such as 570 'rgb(80,100,220)'; or as rgb plus alpha (transparency) such 571 as 'rgba('74,144,226,0.60)'. 572 highlightColor : `str` 573 Color for highlighted footprints 575 Color for selected footprints 576 style : {'fill', 'outline'} 577 Style of footprints display, filled or outline 579 Column at which to insert the "family_id" and "category" columns 581 Name of footprints layer string, to concatenate with the frame 582 Re-using the layer_string will overwrite the previous table and 585 Title of catalog, to concatenate with the frame 588 with BytesIO()
as fd:
589 footprintTable.to_xml(fd)
590 tableval = self.
_client.upload_data(fd,
'UNKNOWN')
591 self.
_client.overlay_footprints(footprint_file=tableval,
592 title=titleString + str(self.display.frame),
593 footprint_layer_id=layerString + str(self.display.frame),
594 plot_id=str(self.display.frame),
596 highlightColor=highlightColor,
597 selectColor=selectColor,
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
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 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