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)
167 extraParams = dict(Title=title,
169 PredefinedOverlayIds=
' ',
170 viewer_id=
'image-' +
str(self.frame))
175 extraParams[
'InitZoomLevel'] = self.
_lastZoom 176 extraParams[
'ZoomType'] =
'LEVEL' 178 extraParams[
'InitialCenterPosition'] =
'{0:.3f};{1:.3f};PIXEL'.
format(
183 ret = _fireflyClient.show_fits(self.
_fireflyFitsID, plot_id=
str(self.display.frame),
186 if not ret[
"success"]:
187 raise RuntimeError(
"Display of image failed")
191 print(
'displaying mask')
192 with tempfile.NamedTemporaryFile()
as fdm:
193 displayLib.writeFitsImage(fdm.name, mask, wcs, title)
198 maskPlaneDict = mask.getMaskPlaneDict()
199 for k, v
in maskPlaneDict.items():
204 if (((1 << self.
_maskDict[k]) & usedPlanes)
and 208 _fireflyClient.add_mask(bit_number=self.
_maskDict[k],
210 plot_id=
str(self.display.frame),
219 def _remove_masks(self):
220 """Remove mask layers""" 222 _fireflyClient.remove_mask(plot_id=
str(self.display.frame), mask_id=k)
225 def _buffer(self, enable=True):
226 """!Enable or disable buffering of writes to the display 227 param enable True or False, as appropriate 232 """!Flush any I/O buffers 238 print(
"Flushing %d regions" % len(self.
_regions))
242 _fireflyClient.add_region_data(region_data=self.
_regions, plot_id=
str(self.display.frame),
246 def _uploadTextData(self, regions):
253 """Called when the device is closed""" 255 print(
"Closing firefly device %s" % (self.display.frame
if self.display
else "[None]"))
256 if _fireflyClient
is not None:
257 _fireflyClient.disconnect()
258 _fireflyClient.session.close()
260 def _dot(self, symb, c, r, size, ctype, fontFamily="helvetica", textAngle=None):
261 """Draw a symbol onto the specified DS9 frame at (col,row) = (c,r) [0-based coordinates] 267 @:Mxx,Mxy,Myy Draw an ellipse with moments (Mxx, Mxy, Myy) (argument size is ignored) 268 An object derived from afwGeom.ellipses.BaseCore Draw the ellipse (argument size is ignored) 269 Any other value is interpreted as a string to be drawn. Strings obey the fontFamily (which may be extended 270 with other characteristics, e.g. "times bold italic". Text will be drawn rotated by textAngle (textAngle 271 is ignored otherwise). 273 N.b. objects derived from BaseCore include Axes and Quadrupole. 275 self.
_uploadTextData(ds9Regions.dot(symb, c, r, size, ctype, fontFamily, textAngle))
277 def _drawLines(self, points, ctype):
278 """Connect the points, a list of (col,row) 279 Ctype is the name of a colour (e.g. 'red')""" 284 """Erase all overlays on the image""" 288 _fireflyClient.delete_region_layer(self.
_regionLayerId, plot_id=
str(self.display.frame))
290 def _setCallback(self, what, func):
291 if func != interface.noop_callback:
293 status = _fireflyClient.add_extension(
'POINT' if False else 'AREA_SELECT', title=what,
294 plot_id=
str(self.display.frame),
296 if not status[
'success']:
298 except Exception
as e:
299 raise RuntimeError(
"Cannot set callback. Browser must be (re)opened " +
301 (_fireflyClient.url_bw,
302 _fireflyClient.channel, e))
305 """Return an event generated by a keypress or mouse click 307 ev = interface.Event(
"q")
310 print(
"virtual[%s]._getEvent() -> %s" % (self.display.frame, ev))
317 def _scale(self, algorithm, min, max, unit=None, *args, **kwargs):
318 """Scale the image stretch and limits 323 stretch algorithm, e.g. 'linear', 'log', 'loglog', 'equal', 'squared', 324 'sqrt', 'asinh', powerlaw_gamma' 325 min : `float` or `str` 326 lower limit, or 'minmax' for full range, or 'zscale' 327 max : `float` or `str` 328 upper limit; overrriden if min is 'minmax' or 'zscale' 330 unit for min and max. 'percent', 'absolute', 'sigma'. 331 if not specified, min and max are presumed to be in 'absolute' units. 333 *args, **kwargs : additional position and keyword arguments. 334 The options are shown below: 336 **Q** : `float`, optional 337 The asinh softening parameter for asinh stretch. 338 Use Q=0 for linear stretch, increase Q to make brighter features visible. 339 When not specified or None, Q is calculated by Firefly to use full color range. 341 The gamma value for power law gamma stretch (default 2.0) 342 **zscale_contrast** : `int`, optional 343 Contrast parameter in percent for zscale algorithm (default 25) 344 **zscale_samples** : `int`, optional 345 Number of samples for zscale algorithm (default 600) 346 **zscale_samples_perline** : `int`, optional 347 Number of samples per line for zscale algorithm (default 120) 349 stretch_algorithms = (
'linear',
'log',
'loglog',
'equal',
'squared',
'sqrt',
350 'asinh',
'powerlaw_gamma')
351 interval_methods = (
'percent',
'maxmin',
'absolute',
'zscale',
'sigma')
357 algorithm = dict((a.lower(), a)
for a
in stretch_algorithms).get(algorithm.lower(), algorithm)
359 if algorithm
not in stretch_algorithms:
360 raise FireflyError(
'Algorithm %s is invalid; please choose one of "%s"' %
361 (algorithm,
'", "'.join(stretch_algorithms)))
368 kwargs[
'asinh_q_value'] = kwargs[
'Q']
371 if 'gamma' in kwargs:
372 kwargs[
'gamma_value'] = kwargs[
'gamma']
376 interval_type =
'percent' 379 elif min ==
'zscale':
380 interval_type =
'zscale' 387 units = (
'percent',
'absolute',
'sigma')
388 if unit
not in units:
389 raise FireflyError(
'Unit %s is invalid; please choose one of "%s"' % (unit,
'", "'.join(units)))
392 interval_type =
'sigma' 393 elif unit ==
'absolute' and interval_type
is None:
394 interval_type =
'absolute' 395 elif unit ==
'percent':
396 interval_type =
'percent' 402 if interval_type
not in interval_methods:
403 raise FireflyError(
'Interval method %s is invalid' % interval_type)
406 if interval_type !=
'zscale':
407 rval = _fireflyClient.set_stretch(
str(self.display.frame), stype=interval_type,
408 algorithm=algorithm, lower_value=min,
409 upper_value=max, **kwargs)
411 if 'zscale_contrast' not in kwargs:
412 kwargs[
'zscale_contrast'] = 25
413 if 'zscale_samples' not in kwargs:
414 kwargs[
'zscale_samples'] = 600
415 if 'zscale_samples_perline' not in kwargs:
416 kwargs[
'zscale_samples_perline'] = 120
417 rval = _fireflyClient.set_stretch(
str(self.display.frame), stype=
'zscale',
418 algorithm=algorithm, **kwargs)
420 if 'rv_string' in rval:
423 def _setMaskTransparency(self, transparency, maskName):
424 """Specify mask transparency (percent); or None to not set it when loading masks""" 425 if maskName
is not None:
426 masklist = [maskName]
428 masklist =
set(self.
_maskIds +
list(self.display._defaultMaskPlaneColor.keys()))
431 _fireflyClient.dispatch(action_type=
'ImagePlotCntlr.overlayPlotChangeAttributes',
432 payload={
'plotId':
str(self.display.frame),
434 'attributes': {
'opacity': 1.0 - transparency/100.},
437 def _getMaskTransparency(self, maskName):
438 """Return the current mask's transparency""" 444 def _setMaskPlaneColor(self, maskName, color):
445 """Specify mask color """ 446 _fireflyClient.remove_mask(plot_id=
str(self.display.frame),
449 if (color.lower() !=
'ignore'):
450 _fireflyClient.add_mask(bit_number=self.
_maskDict[maskName],
452 plot_id=
str(self.display.frame),
458 """Show the requested window""" 459 if self.
_client.render_tree_id
is not None:
464 localbrowser, url = _fireflyClient.launch_browser(verbose=self.verbose)
465 if not localbrowser
and not self.verbose:
466 _fireflyClient.display_url()
472 def _zoom(self, zoomfac):
473 """Zoom display by specified amount 478 zoom level in screen pixels per image pixel 481 _fireflyClient.set_zoom(plot_id=
str(self.display.frame), factor=zoomfac)
483 def _pan(self, colc, rowc):
484 """Pan to specified pixel coordinates 489 column and row in units of pixels (zero-based convention, 490 with the xy0 already subtracted off) 492 self.
_lastPan = [colc+0.5, rowc+0.5]
494 _fireflyClient.set_pan(plot_id=
str(self.display.frame), x=colc, y=rowc)
499 """Get the instance of FireflyClient for this display 503 `firefly_client.FireflyClient` 504 Instance of FireflyClient used by this display 509 """Reinitialize the viewer 514 """Reset the layout of the Firefly Slate browser 516 Clears the display and adds Slate cells to display image in upper left, 517 plot area in upper right, and plots stretch across the bottom 520 self.
_client.add_cell(row=2, col=0, width=4, height=2, element_type=
'tables',
522 self.
_client.add_cell(row=0, col=0, width=2, height=3, element_type=
'images',
523 cell_id=
'image-%s' %
str(self.display.frame))
524 self.
_client.add_cell(row=0, col=2, width=2, height=3, element_type=
'xyPlots',
528 highlightColor='cyan', selectColor=
'orange',
529 style=
'fill', layerString=
'detection footprints ',
530 titleString=
'catalog footprints '):
531 """Overlay outlines of footprints from a catalog 533 Overlay outlines of LSST footprints from the input catalog. The colors 534 and style can be specified as parameters, and the base color and style 535 can be changed in the Firefly browser user interface. 539 catalog : `lsst.afw.table.SourceCatalog` 540 Source catalog from which to display footprints. 542 Color for footprints overlay. Colors can be specified as a name 543 like 'cyan' or afwDisplay.RED; as an rgb value such as 544 'rgb(80,100,220)'; or as rgb plus alpha (transparency) such 545 as 'rgba('74,144,226,0.60)'. 546 highlightColor : `str` 547 Color for highlighted footprints 549 Color for selected footprints 550 style : {'fill', 'outline'} 551 Style of footprints display, filled or outline 553 Column at which to insert the "family_id" and "category" columns 555 Name of footprints layer string, to concatenate with the frame 556 Re-using the layer_string will overwrite the previous table and 559 Title of catalog, to concatenate with the frame 562 with BytesIO()
as fd:
563 footprintTable.to_xml(fd)
564 tableval = self.
_client.upload_data(fd,
'UNKNOWN')
565 self.
_client.overlay_footprints(footprint_file=tableval,
566 title=titleString +
str(self.display.frame),
567 footprint_layer_id=layerString +
str(self.display.frame),
568 plot_id=
str(self.display.frame),
570 highlightColor=highlightColor,
571 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