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:
88 if (
'html_file' not in kwargs)
and (
'FIREFLY_HTML' not in os.environ):
89 kwargs[
'html_file'] =
'slate.html' 92 _fireflyClient = firefly_client.FireflyClient(channel=name, **kwargs)
94 _fireflyClient = firefly_client.FireflyClient(url,
95 channel=name, **kwargs)
96 except (HandshakeError, gaierror)
as e:
97 raise RuntimeError(
"Unable to connect to %s: %s" % (url
or '', e))
100 localbrowser, browser_url = _fireflyClient.launch_browser(verbose=self.verbose)
101 if not localbrowser
and not self.verbose:
102 _fireflyClient.display_url()
104 print(
'localbrowser: ', localbrowser,
' browser_url: ', browser_url)
107 except Exception
as e:
108 raise RuntimeError(
"Cannot add listener. Browser must be connected" +
110 (_fireflyClient.get_firefly_url(), e))
118 self.
_channel = _fireflyClient.channel
119 self.
_url = _fireflyClient.get_firefly_url()
129 def _getRegionLayerId(self):
130 return "lsstRegions%s" % self.display.frame
if self.display
else "None" 132 def _clearImage(self):
133 """Delete the current image in the Firefly viewer 136 action_type=
'ImagePlotCntlr.deletePlotView',
137 payload=dict(plotId=
str(self.display.frame)))
139 def _mtv(self, image, mask=None, wcs=None, title=""):
140 """Display an Image and/or Mask on a Firefly display 143 title =
str(self.display.frame)
146 print(
'displaying image')
149 with tempfile.NamedTemporaryFile()
as fd:
150 displayLib.writeFitsImage(fd.name, image, wcs, title)
155 extraParams = dict(Title=title,
157 PredefinedOverlayIds=
' ',
158 viewer_id=
'image-' +
str(self.frame))
163 extraParams[
'InitZoomLevel'] = self.
_lastZoom 164 extraParams[
'ZoomType'] =
'LEVEL' 166 extraParams[
'InitialCenterPosition'] =
'{0:.3f};{1:.3f};PIXEL'.
format(
171 ret = _fireflyClient.show_fits(self.
_fireflyFitsID, plot_id=
str(self.display.frame),
174 if not ret[
"success"]:
175 raise RuntimeError(
"Display of image failed")
179 print(
'displaying mask')
180 with tempfile.NamedTemporaryFile()
as fdm:
181 displayLib.writeFitsImage(fdm.name, mask, wcs, title)
186 maskPlaneDict = mask.getMaskPlaneDict()
187 for k, v
in maskPlaneDict.items():
192 if (((1 << self.
_maskDict[k]) & usedPlanes)
and 196 _fireflyClient.add_mask(bit_number=self.
_maskDict[k],
198 plot_id=
str(self.display.frame),
207 def _remove_masks(self):
208 """Remove mask layers""" 210 _fireflyClient.remove_mask(plot_id=
str(self.display.frame), mask_id=k)
213 def _buffer(self, enable=True):
214 """!Enable or disable buffering of writes to the display 215 param enable True or False, as appropriate 220 """!Flush any I/O buffers 226 print(
"Flushing %d regions" % len(self.
_regions))
230 _fireflyClient.add_region_data(region_data=self.
_regions, plot_id=
str(self.display.frame),
234 def _uploadTextData(self, regions):
241 """Called when the device is closed""" 243 print(
"Closing firefly device %s" % (self.display.frame
if self.display
else "[None]"))
244 if _fireflyClient
is not None:
245 _fireflyClient.disconnect()
246 _fireflyClient.session.close()
248 def _dot(self, symb, c, r, size, ctype, fontFamily="helvetica", textAngle=None):
249 """Draw a symbol onto the specified DS9 frame at (col,row) = (c,r) [0-based coordinates] 255 @:Mxx,Mxy,Myy Draw an ellipse with moments (Mxx, Mxy, Myy) (argument size is ignored) 256 An object derived from afwGeom.ellipses.BaseCore Draw the ellipse (argument size is ignored) 257 Any other value is interpreted as a string to be drawn. Strings obey the fontFamily (which may be extended 258 with other characteristics, e.g. "times bold italic". Text will be drawn rotated by textAngle (textAngle 259 is ignored otherwise). 261 N.b. objects derived from BaseCore include Axes and Quadrupole. 263 self.
_uploadTextData(ds9Regions.dot(symb, c, r, size, ctype, fontFamily, textAngle))
265 def _drawLines(self, points, ctype):
266 """Connect the points, a list of (col,row) 267 Ctype is the name of a colour (e.g. 'red')""" 272 """Erase all overlays on the image""" 276 _fireflyClient.delete_region_layer(self.
_regionLayerId, plot_id=
str(self.display.frame))
278 def _setCallback(self, what, func):
279 if func != interface.noop_callback:
281 status = _fireflyClient.add_extension(
'POINT' if False else 'AREA_SELECT', title=what,
282 plot_id=
str(self.display.frame),
284 if not status[
'success']:
286 except Exception
as e:
287 raise RuntimeError(
"Cannot set callback. Browser must be (re)opened " +
289 (_fireflyClient.url_bw,
290 _fireflyClient.channel, e))
293 """Return an event generated by a keypress or mouse click 295 ev = interface.Event(
"q")
298 print(
"virtual[%s]._getEvent() -> %s" % (self.display.frame, ev))
305 def _scale(self, algorithm, min, max, unit=None, *args, **kwargs):
306 """Scale the image stretch and limits 311 stretch algorithm, e.g. 'linear', 'log', 'loglog', 'equal', 'squared', 312 'sqrt', 'asinh', powerlaw_gamma' 313 min : `float` or `str` 314 lower limit, or 'minmax' for full range, or 'zscale' 315 max : `float` or `str` 316 upper limit; overrriden if min is 'minmax' or 'zscale' 318 unit for min and max. 'percent', 'absolute', 'sigma'. 319 if not specified, min and max are presumed to be in 'absolute' units. 321 *args, **kwargs : additional position and keyword arguments. 322 The options are shown below: 324 **Q** : `float`, optional 325 The asinh softening parameter for asinh stretch. 326 Use Q=0 for linear stretch, increase Q to make brighter features visible. 327 When not specified or None, Q is calculated by Firefly to use full color range. 329 The gamma value for power law gamma stretch (default 2.0) 330 **zscale_contrast** : `int`, optional 331 Contrast parameter in percent for zscale algorithm (default 25) 332 **zscale_samples** : `int`, optional 333 Number of samples for zscale algorithm (default 600) 334 **zscale_samples_perline** : `int`, optional 335 Number of samples per line for zscale algorithm (default 120) 337 stretch_algorithms = (
'linear',
'log',
'loglog',
'equal',
'squared',
'sqrt',
338 'asinh',
'powerlaw_gamma')
339 interval_methods = (
'percent',
'maxmin',
'absolute',
'zscale',
'sigma')
345 algorithm = dict((a.lower(), a)
for a
in stretch_algorithms).get(algorithm.lower(), algorithm)
347 if algorithm
not in stretch_algorithms:
348 raise FireflyError(
'Algorithm %s is invalid; please choose one of "%s"' %
349 (algorithm,
'", "'.join(stretch_algorithms)))
356 kwargs[
'asinh_q_value'] = kwargs[
'Q']
359 if 'gamma' in kwargs:
360 kwargs[
'gamma_value'] = kwargs[
'gamma']
364 interval_type =
'percent' 367 elif min ==
'zscale':
368 interval_type =
'zscale' 375 units = (
'percent',
'absolute',
'sigma')
376 if unit
not in units:
377 raise FireflyError(
'Unit %s is invalid; please choose one of "%s"' % (unit,
'", "'.join(units)))
380 interval_type =
'sigma' 381 elif unit ==
'absolute' and interval_type
is None:
382 interval_type =
'absolute' 383 elif unit ==
'percent':
384 interval_type =
'percent' 390 if interval_type
not in interval_methods:
391 raise FireflyError(
'Interval method %s is invalid' % interval_type)
394 if interval_type
is not 'zscale':
395 rval = _fireflyClient.set_stretch(
str(self.display.frame), stype=interval_type,
396 algorithm=algorithm, lower_value=min,
397 upper_value=max, **kwargs)
399 if 'zscale_contrast' not in kwargs:
400 kwargs[
'zscale_contrast'] = 25
401 if 'zscale_samples' not in kwargs:
402 kwargs[
'zscale_samples'] = 600
403 if 'zscale_samples_perline' not in kwargs:
404 kwargs[
'zscale_samples_perline'] = 120
405 rval = _fireflyClient.set_stretch(
str(self.display.frame), stype=
'zscale',
406 algorithm=algorithm, **kwargs)
408 if 'rv_string' in rval:
411 def _setMaskTransparency(self, transparency, maskName):
412 """Specify mask transparency (percent); or None to not set it when loading masks""" 413 if maskName
is not None:
414 masklist = [maskName]
416 masklist =
set(self.
_maskIds +
list(self.display._defaultMaskPlaneColor.keys()))
419 _fireflyClient.dispatch_remote_action(channel=_fireflyClient.channel,
420 action_type=
'ImagePlotCntlr.overlayPlotChangeAttributes',
421 payload={
'plotId':
str(self.display.frame),
423 'attributes': {
'opacity': 1.0 - transparency/100.},
426 def _getMaskTransparency(self, maskName):
427 """Return the current mask's transparency""" 433 def _setMaskPlaneColor(self, maskName, color):
434 """Specify mask color """ 435 _fireflyClient.remove_mask(plot_id=
str(self.display.frame),
438 if (color.lower() !=
'ignore'):
439 _fireflyClient.add_mask(bit_number=self.
_maskDict[maskName],
441 plot_id=
str(self.display.frame),
447 """Show the requested window""" 448 localbrowser, url = _fireflyClient.launch_browser(verbose=self.verbose)
449 if not localbrowser
and not self.verbose:
450 _fireflyClient.display_url()
455 def _zoom(self, zoomfac):
456 """Zoom display by specified amount 461 zoom level in screen pixels per image pixel 464 _fireflyClient.set_zoom(plot_id=
str(self.display.frame), factor=zoomfac)
466 def _pan(self, colc, rowc):
467 """Pan to specified pixel coordinates 472 column and row in units of pixels (zero-based convention, 473 with the xy0 already subtracted off) 475 self.
_lastPan = [colc+0.5, rowc+0.5]
477 _fireflyClient.set_pan(plot_id=
str(self.display.frame), x=colc, y=rowc)
482 """Get the instance of FireflyClient for this display 486 `firefly_client.FireflyClient` 487 Instance of FireflyClient used by this display 492 """Reinitialize the viewer 497 """Reset the layout of the Firefly Slate browser 499 Clears the display and adds Slate cells to display image in upper left, 500 plot area in upper right, and plots stretch across the bottom 503 self.
_client.add_cell(row=2, col=0, width=4, height=2, element_type=
'tables',
505 self.
_client.add_cell(row=0, col=0, width=2, height=3, element_type=
'images',
506 cell_id=
'image-%s' %
str(self.display.frame))
507 self.
_client.add_cell(row=0, col=2, width=2, height=3, element_type=
'xyPlots',
511 highlightColor='cyan', selectColor=
'orange',
512 style=
'fill', layerString=
'detection footprints ',
513 titleString=
'catalog footprints '):
514 """Overlay outlines of footprints from a catalog 516 Overlay outlines of LSST footprints from the input catalog. The colors 517 and style can be specified as parameters, and the base color and style 518 can be changed in the Firefly browser user interface. 522 catalog : `lsst.afw.table.SourceCatalog` 523 Source catalog from which to display footprints. 525 Color for footprints overlay. Colors can be specified as a name 526 like 'cyan' or afwDisplay.RED; as an rgb value such as 527 'rgb(80,100,220)'; or as rgb plus alpha (transparency) such 528 as 'rgba('74,144,226,0.60)'. 529 highlightColor : `str` 530 Color for highlighted footprints 532 Color for selected footprints 533 style : {'fill', 'outline'} 534 Style of footprints display, filled or outline 536 Column at which to insert the "family_id" and "category" columns 538 Name of footprints layer string, to concatenate with the frame 539 Re-using the layer_string will overwrite the previous table and 542 Title of catalog, to concatenate with the frame 545 with BytesIO()
as fd:
546 footprintTable.to_xml(fd)
547 tableval = self.
_client.upload_data(fd,
'UNKNOWN')
548 self.
_client.overlay_footprints(footprint_file=tableval,
549 title=titleString +
str(self.display.frame),
550 footprint_layer_id=layerString +
str(self.display.frame),
551 plot_id=
str(self.display.frame),
553 highlightColor=highlightColor,
554 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