diff --git a/docs/figure_file_format.rst b/docs/figure_file_format.rst index 1bd45fb74..77cac2c44 100644 --- a/docs/figure_file_format.rst +++ b/docs/figure_file_format.rst @@ -36,7 +36,7 @@ This is an example of a minimal OMERO.figure JSON file:: { // see older versions below - "version": 5, + "version": 6, "panels": [ { // position of the panel on the page @@ -82,6 +82,11 @@ Optional settings for each panel:: // options are omero.model.LengthI.SYMBOLS.keys() "pixel_size_x_unit": 'MICROMETER', + // pixel size z to show z position in labels + "pixel_size_z": 0.32, + "pixel_size_z_symbol": 'µm', // µm by default + "pixel_size_z_unit": 'MICROMETER', + // show a scalebar "scalebar": { "show": true, @@ -108,12 +113,22 @@ Optional settings for each panel:: "color": "00FF00" }, { - // 'live' timestamps, 'time' one of: index (show 1-based T index), milliseconds, + // Dynamic properties: text in labels in the form '[property.format]' + // are dynamically replaced by the specified values + + // for 'time' property, 'format' one of: index (show 1-based T index), milliseconds, // secs, mins:secs, mins, hrs:mins, hrs:mins:secs, - "time": "milliseconds", + "text": "[time.milliseconds]", "size": "12", "position": "topleft", "color": "FFFFFF" + }, + { + // for 'x' and 'y' property, 'format' one of: pixel, unit + "text": "X: [x.pixel] - Y: [y.pixel]", + "size": "12", + "position": "topright", + "color": "FFFFFF" } ], @@ -137,6 +152,8 @@ Optional settings for each panel:: // panel rotation in degrees clockwise rotation: 0, + // rotation symbol to display in label + rotation_symbol:'°', Optional settings for the top-level figure object. If not specified, @@ -208,6 +225,11 @@ that lines will not appear thicker on a panel when it is zoomed in. Supported Sh Version history ---------------- +New in version 6: + +- 'label': 'time':'seconds' changed to 'text':'[time.seconds]' (for all timestamp formats) +- 'panel': z pixel properties added ('pixel_size_z', 'pixel_size_z_symbol', 'pixel_size_z_unit') + New in version 5: - `scalebar`: added 'pixel_size_x_unit': "MICROMETER". diff --git a/omero_figure/scripts/omero/figure_scripts/Figure_To_Pdf.py b/omero_figure/scripts/omero/figure_scripts/Figure_To_Pdf.py index 0e50d2a7d..53b422ef3 100644 --- a/omero_figure/scripts/omero/figure_scripts/Figure_To_Pdf.py +++ b/omero_figure/scripts/omero/figure_scripts/Figure_To_Pdf.py @@ -28,6 +28,7 @@ import zipfile from math import atan2, atan, sin, cos, sqrt, radians, floor, ceil from copy import deepcopy +import re from omero.model import ImageAnnotationLinkI, ImageI, LengthI import omero.scripts as scripts @@ -1092,23 +1093,27 @@ def get_time_label_text(self, delta_t, format): is_negative = delta_t < 0 delta_t = abs(delta_t) text = "%d s" % int(round(delta_t)) - if format == "milliseconds": + if format in ["milliseconds", "ms"]: text = "%s ms" % int(round(delta_t * 1000)) - elif format == "mins": + elif format in ["secs", "seconds", "s"]: + text = "%d s" % int(round(delta_t)) + elif format in ["mins", "minutes", "m"]: text = "%s mins" % int(round(delta_t / 60)) - elif format == "mins:secs": + elif format in ["mins:secs", "m:s"]: m = int(delta_t // 60) s = round(delta_t % 60) text = "%s:%02d" % (m, s) - elif format == "hrs:mins": + elif format in ["hrs:mins", "h:m"]: h = int(delta_t // 3600) m = int(round((delta_t % 3600) / 60)) text = "%s:%02d" % (h, m) - elif format == "hrs:mins:secs": + elif format in ["hrs:mins:secs", "h:m:s"]: h = int(delta_t // 3600) m = (delta_t % 3600) // 60 s = round(delta_t % 60) text = "%s:%02d:%02d" % (h, m, s) + else: # Format unknown + return "" if text in ["0 s", "0:00", "0 mins", "0:00:00"]: is_negative = False return ('-' if is_negative else '') + text @@ -1136,6 +1141,8 @@ def draw_labels(self, panel, page): width = panel['width'] height = panel['height'] + viewport_region = self.get_crop_region(panel) + # Handle page offsets x = x - page['x'] y = y - page['y'] @@ -1148,18 +1155,122 @@ def draw_labels(self, panel, page): 'topleft': [], 'topright': [], 'bottomleft': [], 'bottomright': []} + parse_re = re.compile(r"\[.+?\]") for l in labels: - if 'text' not in l: - the_t = panel['theT'] - timestamps = panel.get('deltaT') - if l.get('time') == "index": - l['text'] = str(the_t + 1) - elif timestamps and panel['theT'] < len(timestamps): - d_t = panel['deltaT'][the_t] - l['text'] = self.get_time_label_text(d_t, l['time']) - else: - continue - + # Substitution of special label by their values + new_text = [] + last_idx = 0 + for item in parse_re.finditer(l['text']): + new_text.append(l['text'][last_idx:item.start()]) + expr = item.group()[1:-1].split(".") + label_value = "" + + if expr[0] in ["time", "t"]: + the_t = panel['theT'] + timestamps = panel.get('deltaT') + # default to index + if len(expr) == 1 or expr[1] == "index": + label_value = str(the_t + 1) + else: + if timestamps and the_t < len(timestamps): + d_t = timestamps[the_t] + else: + d_t = 0 + label_value = self.get_time_label_text(d_t, expr[1]) + + elif expr[0] == "image": + format = expr[1] if len(expr) > 1 else "name" + if format == "name": + label_value = panel['name'].split('/')[-1] + elif format == "id": + label_value = str(panel['imageId']) + # Escaping "_" for markdown + label_value = label_value.replace("_", "\\_") + + elif expr[0] == "dataset": + format = expr[1] if len(expr) > 1 else "name" + if format == "name": + if panel['datasetName']: + label_value = panel['datasetName'] + else: + label_value = "No/Many Datasets" + elif format == "id": + if panel['datasetId']: + label_value = panel['datasetName'] + else: + label_value = "null" + # Escaping "_" for markdown + label_value = label_value.replace("_", "\\_") + + elif expr[0] in ['x', 'y', 'z', 'width', 'height', + 'w', 'h', 'rotation', 'rot']: + format = expr[1] if len(expr) > 1 else "pixel" + if format == "px": + format = "pixel" + prop = expr[0] + if prop == "w": + prop = "width" + elif prop == "h": + prop = "height" + elif prop == "rot": + prop = "rotation" + + if prop == "z": + size_z = panel.get('sizeZ') + pixel_size_z = panel.get('pixel_size_z') + z_symbol = panel.get('pixel_size_z_symbol') + if pixel_size_z is None: + pixel_size_z = 0 + + if ("z_projection" in panel.keys() + and panel["z_projection"]): + z_start, z_end = panel["z_start"], panel["z_end"] + if format == "pixel": + label_value = (str(z_start + 1) + "-" + + str(z_end + 1)) + elif format == "unit" and size_z: + z_start = f"{(z_start * pixel_size_z):.2f}" + z_end = f"{(z_end * pixel_size_z):.2f}" + label_value = (z_start + " " + z_symbol + " - " + + z_end + " " + z_symbol) + else: + the_z = panel['theZ'] if panel['theZ'] else 0 + if format == "pixel": + label_value = str(the_z + 1) + elif (format == "unit" and size_z + and the_z < size_z): + z_pos = f"{(the_z * pixel_size_z):.2f}" + label_value = (z_pos + " " + z_symbol) + + elif prop == "rotation": + label_value = (str(int(panel["rotation"])) + + panel['rotation_symbol']) + + else: + value = viewport_region[prop] + if format == "pixel": + label_value = str(int(value)) + elif format == "unit": + if prop in ['x', 'width']: + scale = panel['pixel_size_x'] + elif prop in ['y', 'height']: + scale = panel['pixel_size_y'] + rounded = f"{(value * scale):.2f}" + label_value = ("" + rounded + + " " + panel['pixel_size_x_symbol']) + + elif expr[0] in ["channels", "c"]: + label_value = [] + for channel in panel["channels"]: + if channel["active"]: + label_value.append(channel["label"]) + label_value = " ".join(label_value) + + new_text.append(label_value if label_value else item.group()) + last_idx += item.end() + + new_text.append(l['text'][last_idx:]) + l['text'] = "".join(new_text) pos = l['position'] l['size'] = int(l['size']) # make sure 'size' is number # If page is black and label is black, make label white diff --git a/omero_figure/static/figure/css/figure.css b/omero_figure/static/figure/css/figure.css index eec7f13ba..8bd5b9ec4 100644 --- a/omero_figure/static/figure/css/figure.css +++ b/omero_figure/static/figure/css/figure.css @@ -167,16 +167,6 @@ padding: 3px 15px; } - .markdown-info { - padding: 3px 12px; - color: #aaa; - text-align: right; - padding-left: 40px; - background: url(../images/markdown_light32x20.png) 0% center no-repeat; - } - .markdown-info:hover { - background: url(../images/markdown_dark32x20.png) 0% center no-repeat; - } .legend-container .markdown-info { display: none; } @@ -184,12 +174,6 @@ display: block; } - #labelsTab .markdown-info { - height: 18px; - margin: 9px 9px 0; - float: left" - } - /* hide appropriate collapse/hide button */ .legend-collapsed .collapse-legend{ display: none; diff --git a/omero_figure/templates/figure/index.html b/omero_figure/templates/figure/index.html index 51c2ee3b7..2b8ab2ece 100644 --- a/omero_figure/templates/figure/index.html +++ b/omero_figure/templates/figure/index.html @@ -514,9 +514,10 @@