Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic labels with regex #472

Merged
merged 35 commits into from
Aug 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e79f06f
Special label parsed with regex
Tom-TBT Jul 11, 2022
811cf2f
Regex label implementation
Tom-TBT Jul 11, 2022
734b474
Added name-image and name-dataset to special label
Tom-TBT Jul 11, 2022
6c23521
Implemented dynamic label for image and dataset name
Tom-TBT Jul 11, 2022
15d474c
Added viewport (x,y,width,height,rotation) to special label
Tom-TBT Jul 11, 2022
b11ee37
Implement ROI dynamic label for pdf export
Tom-TBT Jul 11, 2022
3a820d7
Added Z-index and depth in units to dynamic label
Tom-TBT Jul 11, 2022
3bf795b
Implement Z dynamic label for pdf export
Tom-TBT Jul 11, 2022
bd30d8f
Adding examples to label dropdown list
Tom-TBT Jul 11, 2022
9ce71eb
Fixed import renaming accident
Tom-TBT Jul 11, 2022
f4dd9b0
flake8 style fixes
Tom-TBT Jul 12, 2022
e47df00
End of label text appending fix
Tom-TBT Jul 13, 2022
db297a0
Set depth value to zero when pixel_size_z not defined
Tom-TBT Jul 13, 2022
ab8adeb
Dynamic label for channels
Tom-TBT Jul 13, 2022
b3974c4
Fix: Invalid negative time-label display
Tom-TBT Jul 13, 2022
f6ba988
flake8 style fix
Tom-TBT Jul 13, 2022
a0b362f
Dynamic label name recast
Tom-TBT Jul 22, 2022
25acc00
Figure export for label recast
Tom-TBT Jul 22, 2022
c2fdcd0
Z-projection label
Tom-TBT Jul 22, 2022
803d0f7
PDF export Z-projection
Tom-TBT Jul 22, 2022
505f9f5
Z project display tweek
Tom-TBT Jul 22, 2022
61ca726
Undefined Z label handling
Tom-TBT Jul 25, 2022
0cdac47
Version transform: z scale
Tom-TBT Jul 25, 2022
c8bd627
Version transform: time label
Tom-TBT Jul 25, 2022
12c8113
Fixed time-padding deletion
Tom-TBT Aug 1, 2022
ceaf021
Fixed format/value between app and PDF-export
Tom-TBT Aug 1, 2022
3aa314e
Added dynamic label doc to the label info window
Tom-TBT Aug 2, 2022
47b80ab
Editing UI to open Label formatting info
Tom-TBT Aug 2, 2022
3aa4b78
Updated label tips
Tom-TBT Aug 2, 2022
965466f
Removed image&dataset id from drop-down list
Tom-TBT Aug 2, 2022
5f9df6f
Separate labels plural
Tom-TBT Aug 2, 2022
b418624
Updated label dropdown list and info dialog
Tom-TBT Aug 8, 2022
f6a5c27
Merge branch 'master' of https://github.com/Tom-TBT/omero-figure
Tom-TBT Aug 8, 2022
e306f03
version 6 update
Tom-TBT Aug 9, 2022
e7a18ae
Tip dialog revision
Tom-TBT Aug 9, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions docs/figure_file_format.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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"
}
],

Expand All @@ -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,
Expand Down Expand Up @@ -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".
Expand Down
143 changes: 127 additions & 16 deletions omero_figure/scripts/omero/figure_scripts/Figure_To_Pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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']
Expand All @@ -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
Expand Down
16 changes: 0 additions & 16 deletions omero_figure/static/figure/css/figure.css
Original file line number Diff line number Diff line change
Expand Up @@ -167,29 +167,13 @@
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;
}
.legend-container .editing .markdown-info {
display: block;
}

#labelsTab .markdown-info {
height: 18px;
margin: 9px 9px 0;
float: left"
}

/* hide appropriate collapse/hide button */
.legend-collapsed .collapse-legend{
display: none;
Expand Down
55 changes: 52 additions & 3 deletions omero_figure/templates/figure/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -514,9 +514,10 @@ <h4 class="modal-title" id="addImagesLabel">Import from JSON</h4>
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Markdown Formatting</h4>
<h4 class="modal-title">Label Formatting</h4>
</div>
<div class="modal-body">
<div class="modal-body" style="max-height:518px">
<h4>Markdown formatting</h4>
<p>
You can use "Markdown" syntax to add formatting to plain text. For example,
**this text will be bold** and this *word* will be italic. NB: Links are not
Expand Down Expand Up @@ -545,6 +546,52 @@ <h4 class="modal-title">Markdown Formatting</h4>
</tr>
<!-- <tr><td>You need to use 2 line breaks between paragraphs</td><td></td></tr> -->
</table>
<h4>Dynamic properties</h4>
<p>
The drop-down menu in the 'Add Labels' form allows you to create labels based on Image metadata.
Labels created with square brackets indicate dynamic properties. The properties within the square
brackets will be dynamically updated upon changes. Additional text can be added outside the square
brackets and will not be affected when the labels are displayed.<br>
The following table shows some examples, including the use of dynamic properties and markdown together.
</p>
<table class="table">
<tr>
<td></td>
<th>This text will be formatted to...</th>
<th>...this text</th></tr>
<tr>
<tr>
<th>X and Y in pixels</th>
<td>X:[x.pixel] - Y:[y.pixel]</td>
<td>X:52 - Y:89</td>
</tr>
<tr>
<th>X and Y in units</th>
<td>X:[x.unit] - Y:[y.unit]</td>
<td>X:0.43 &#181;m - Y:0.23 &#181;m</td>
</tr>
<tr>
<th>Width</th>
<td>Width: [width.pixel]</td>
<td>Width: 400</td>
</tr>
<tr>
<th>Height (with markdown formatting)</th>
<td>*Height*: **[height.pixel]**</td>
<td><i>Height</i>: <strong>300</strong></td>
</tr>
<tr>
<th>Image ID</th>
<td>Image ID: [image.id]</td>
<td>Image ID: 23556</td>
</tr>
<tr>
<th>Dataset ID</th>
<td>Dataset ID: [dataset.id]</td>
<td>Dataset ID: 1654</td>
</tr>
</table>
<h4>Legend</h4>
<p>The figure legend will be included in the PDF info page when the figure is
exported.</p>
</div>
Expand Down Expand Up @@ -1020,7 +1067,9 @@ <h5>Scalebar</h5>
<h5 style="float: left">Add Labels</h5>

<button type="button" class="btn btn-link markdown-info"
title="Use Markdown formatting. Click for more info..."></button>
title="Show label formatting options...">
Tips
</button>
<div class="clearfix"></div>
<form class="new-label-form form-inline" role="form">
</form>
Expand Down
4 changes: 4 additions & 0 deletions omero_figure/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@
# Use query ?image=1&image=2
url(r'^timestamps/$', views.timestamps, name='figure_timestamps'),

# Get Z scale for images
# Use query ?image=1&image=2
url(r'^z_scale/$', views.z_scale, name='figure_z_scale'),

url(r'^roiCount/(?P<image_id>[0-9]+)/$', views.roi_count,
name='figure_roiCount'),

Expand Down
Loading