picture.html
HTML document, ASCII text
1
{% extends "default.html" %}
2
{% block title %}Picture {{ resource.title }} | gigadata{% endblock %}
3
{% macro shape_label(x, y, text) %}
4
{% if text %}
5
<a class="shape-label" style="left: {{ x * 100 }}%; top: {{ y * 100 }}%" href="/object/{{ text }}">
6
{{ text }}
7
</a>
8
{% endif %}
9
{% endmacro %}
10
{% block content %}
11
<div id="picture-view">
12
<x-frame id="picture-actions">
13
<ul class="action-list">
14
<li><a href="{{ resource.origin_url }}">
15
<iconify-icon icon="mdi:web"></iconify-icon>Original source
16
</a></li>
17
<li><a href="/raw/picture/{{ resource.id }}" target="_blank">
18
<iconify-icon icon="mdi:image"></iconify-icon>View separately
19
</a></li>
20
<li><a href="/raw/picture/{{ resource.id }}" download="GigadataPicture_{{ resource.id }}{{ file_extension }}">
21
<iconify-icon icon="mdi:download"></iconify-icon>Download
22
</a></li>
23
<li><a href="/picture/{{ resource.id }}/get-annotations">
24
<iconify-icon icon="ic:baseline-account-tree"></iconify-icon>JSON annotations
25
</a></li>
26
{% if have_permission %}
27
<li><a href="/picture/{{ resource.id }}/annotate">
28
<iconify-icon icon="mdi:vector-point-select"></iconify-icon>Annotate
29
</a></li>
30
<li><a href="/picture/{{ resource.id }}/put-annotations-form">
31
<iconify-icon icon="mdi:file-edit"></iconify-icon>Submit JSON annotations
32
</a></li>
33
<li><a href="/picture/{{ resource.id }}/edit-metadata">
34
<iconify-icon icon="mdi:edit"></iconify-icon>Edit metadata
35
</a></li>
36
{% endif %}
37
{% if current_user %}
38
<li><a href="/picture/{{ resource.id }}/copy">
39
<iconify-icon icon="mdi:content-copy"></iconify-icon>Copy
40
</a></li>
41
{% endif %}
42
{% if have_permission %}
43
<li><details>
44
<summary>
45
<iconify-icon icon="mdi:delete"></iconify-icon>Delete
46
</summary>
47
<a href="/picture/{{ resource.id }}/delete">Confirm deletion</a>
48
</details></li>
49
{% endif %}
50
</ul>
51
</x-frame>
52
<x-frame style="--width: 768px">
53
<h1>{{ resource.title }}</h1>
54
<p>by <a href="/profile/{{ resource.author.username }}">{{ resource.author.formatted_name }}</a></p>
55
<p>{{ resource.description }}</p>
56
{% if resource.replaced_by %}
57
<h2>Obsolete</h2>
58
<p>
59
This picture has been replaced by <a href="/picture/{{ resource.replaced_by.id }}">{{ resource.replaced_by.title }}</a>.
60
</p>
61
{% if have_permission %}
62
<form method="POST" action="/picture/{{ resource.id }}/remove-replacement">
63
<button class="button-flat" type="submit">Remove replacement</button>
64
</form>
65
{% endif %}
66
{% endif %}
67
<x-vbox>
68
<div id="annotation-zone">
69
<img id="annotation-image" src="/raw/picture/{{ resource.id }}" alt="{{ resource.title }}">
70
{% for region in resource.regions %}
71
{% if region.json.type == "bbox" %}
72
<svg class="shape-container-viewonly" viewBox="0 0 {{ size[0] }} {{ size[1] }}">
73
<rect x="{{ region.json.shape.x * size[0] }}"
74
y="{{ region.json.shape.y * size[1] }}"
75
width="{{ region.json.shape.w * size[0] }}"
76
height="{{ region.json.shape.h * size[1] }}"
77
fill="none" class="shape-bbox shape"
78
></rect>
79
{% set centre_x = region.json.shape.x + region.json.shape.w / 2 %}
80
{% set centre_y = region.json.shape.y + region.json.shape.h / 2 %}
81
</svg>
82
{% elif region.json.type == "polygon" %}
83
<svg class="shape-container-viewonly" viewBox="0 0 {{ size[0] }} {{ size[1] }}">
84
<polygon points="{% for point in region.json.shape %}{{ point.x * size[0] }},{{ point.y * size[1] }} {% endfor %}" fill="none" class="shape-polygon shape"></polygon>
85
{% set top = region.json.shape | sort(attribute='y') | last %}
86
{% set left = region.json.shape | sort(attribute='x') | first %}
87
{% set bottom = region.json.shape | sort(attribute='y') | first %}
88
{% set right = region.json.shape | sort(attribute='x') | last %}
89
{% set centre_x = (left.x + right.x) / 2 %}
90
{% set centre_y = (top.y + bottom.y) / 2 %}
91
</svg>
92
{% elif region.json.type == "polyline" %}
93
<svg class="shape-container-viewonly" viewBox="0 0 {{ size[0] }} {{ size[1] }}">
94
<polyline points="{% for point in region.json.shape %}{{ point.x * size[0] }},{{ point.y * size[1] }} {% endfor %}" fill="none" class="shape-polyline shape"></polyline>
95
{# Median point #}
96
{% set centre_x = region.json.shape | map(attribute="x") | median %}
97
{% set centre_y = region.json.shape | map(attribute="y") | median %}
98
</svg>
99
{% elif region.json.type == "point" %}
100
<svg class="shape-container-viewonly" viewBox="0 0 {{ size[0] }} {{ size[1] }}">
101
<circle cx="{{ region.json.shape.x * size[0] }}" cy="{{ region.json.shape.y * size[1] }}" r="0" fill="none" class="shape-point shape"></circle>
102
</svg>
103
{% endif %}
104
{{ shape_label(centre_x, centre_y, region.object_id) }}
105
{% endfor %}
106
</div>
107
<x-buttonbox>
108
<label>
109
<input type="checkbox" id="show-shapes" checked>
110
Show shapes
111
</label>
112
<label>
113
<input type="checkbox" id="show-objects" checked>
114
Show objects
115
</label>
116
</x-buttonbox>
117
{% set licences = resource.licences | map(attribute="licence") | list %}
118
{% set contains = resource.regions | map(attribute="object_id") | set | select | sort | list %}
119
<h2>Ratings ({{ resource.rating_totals.values() | sum }})</h2>
120
<x-hbox>
121
<x-vbox>
122
{% if resource.average_rating %}
123
<div class="rating-bar">
124
{% for i in range(1, 6) %}
125
<div class="rating-bar-segment">
126
<div class="rating-bar-filling" style="width: {{ resource.stars[i-1] }}%"></div>
127
</div>
128
{% endfor %}
129
</div>
130
<p>
131
<span>Average rating:</span>
132
<span>{{ resource.average_rating | round(2) }}</span>
133
from {{ resource.rating_totals.values() | sum }} ratings
134
</p>
135
{% endif %}
136
{% if current_user %}
137
<h3>Your rating</h3>
138
<form id="rating-form" method="POST" action="/picture/{{ resource.id }}/rate">
139
<label>
140
<input name="rating" type="radio" value="0" {% if not own_rating.rating %}checked{% endif %}>
141
Clear rating
142
</label>
143
<div class="star-rating-container">
144
<input type="radio" id="stars-5" name="rating" value="5" title="Perfect" {% if own_rating.rating == 5 %}checked{% endif %}>
145
<label for="stars-5" tabindex="0"><iconify-icon icon="mdi:star" class="star">5 stars</iconify-icon></label>
146
<input type="radio" id="stars-4" name="rating" value="4" title="Good" {% if own_rating.rating == 4 %}checked{% endif %}>
147
<label for="stars-4" tabindex="0"><iconify-icon icon="mdi:star" class="star">4 stars</iconify-icon></label>
148
<input type="radio" id="stars-3" name="rating" value="3" title="OK" {% if own_rating.rating == 3 %}checked{% endif %}>
149
<label for="stars-3" tabindex="0"><iconify-icon icon="mdi:star" class="star">3 stars</iconify-icon></label>
150
<input type="radio" id="stars-2" name="rating" value="2" title="Poor" {% if own_rating.rating == 2 %}checked{% endif %}>
151
<label for="stars-2" tabindex="0"><iconify-icon icon="mdi:star" class="star">2 stars</iconify-icon></label>
152
<input type="radio" id="stars-1" name="rating" value="1" title="Awful" {% if own_rating.rating == 1 %}checked{% endif %}>
153
<label for="stars-1" tabindex="0"><iconify-icon icon="mdi:star" class="star">1 star</iconify-icon></label>
154
</div>
155
<button type="submit">Rate</button>
156
</form>
157
{% endif %}
158
</x-vbox>
159
{% if resource.average_rating %}
160
<ul class="rating-list flexible-space">
161
{% for i in range(5, 0, -1) %}
162
<li style="grid-column-end: {{ resource.rating_totals[i] + 2 }}; background: var(--{{ i }}-star);">
163
<span>{{ i }}:</span>
164
<span>{{ resource.rating_totals[i] }}</span>
165
</li>
166
{% endfor %}
167
</ul>
168
{% else %}
169
<p>No ratings yet.</p>
170
{% endif %}
171
</x-hbox>
172
<h2>Details</h2>
173
<div class="icon-explainer">
174
<span>Type</span>
175
<span>{{ resource.nature.id }}</span>
176
<span>File format</span>
177
<span>{{ resource.file_format }}</span>
178
<span>Size</span>
179
<span>{{ size[0] }}×{{ size[1] }}</span>
180
<span>Number of regions</span>
181
<span>{{ resource.regions | length }}</span>
182
<span>Number of labelled regions</span>
183
<span>{{ resource.regions | selectattr("object_id") | list | length }}</span>
184
<span>Date uploaded</span>
185
<span>{{ resource.timestamp }}</span>
186
</div>
187
{% if contains %}
188
<p>
189
Contains objects: {% for object_id in contains %}
190
<a href="/object/{{ object_id }}">{{ object_id }}</a>{% if not loop.last %}, {% endif %}{% endfor %}
191
</p>
192
{% else %}
193
<p>No labelled regions.</p>
194
{% endif %}
195
<h2>Licensing</h2>
196
<x-hbox style="justify-content: space-between">
197
<small class="picture-licensing-info">
198
Available under:
199
{% for licence in licences %}
200
<a href="{{ licence.info_url }}" target="_blank">
201
{{ licence.title }}
202
</a>
203
{% if not loop.last %}, {% endif %}
204
{% endfor %}
205
</small>
206
<x-vbox class="picture-licence-logos">
207
{% for licence in licences[:6] %}
208
{% if licence.logo_url %}
209
{% if licence.info_url %}
210
<a href="{{ licence.info_url }}" target="_blank" tabindex="-1">
211
{# An equivalent link already exists, only one is focusable #}
212
<img src="{{ licence.logo_url }}" alt="{{ licence.title }}" class="licence-logo">
213
</a>
214
{% else %}
215
<img src="{{ licence.logo_url }}" alt="{{ licence.title }}" class="licence-logo">
216
{% endif %}
217
{% endif %}
218
{% endfor %}
219
{% if licences | length > 6 %}
220
<details>
221
<summary>More</summary>
222
<x-vbox>
223
{% for licence in licences[6:] %}
224
{% if licence.logo_url %}
225
{% if licence.info_url %}
226
<a href="{{ licence.info_url }}" target="_blank" tabindex="-1">
227
{# An equivalent link already exists, only one is focusable #}
228
<img src="{{ licence.logo_url }}" alt="{{ licence.title }}" class="licence-logo">
229
</a>
230
{% else %}
231
<img src="{{ licence.logo_url }}" alt="{{ licence.title }}" class="licence-logo">
232
{% endif %}
233
{% endif %}
234
{% endfor %}
235
</x-vbox>
236
</details>
237
{% endif %}
238
</x-vbox>
239
</x-hbox>
240
<h2>Copies</h2>
241
{% if resource.copies %}
242
<ul class="thumbnail-list">
243
{% for copy in resource.copies %}
244
<li>
245
<a href="/picture/{{ copy.id }}">
246
<div class="annotation-zone">
247
<img src="/raw/picture/{{ copy.id }}" alt="{{ copy.title }}">
248
249
{% for region in copy.regions %}
250
{% if region.json.type == "bbox" %}
251
<svg class="shape-container" viewBox="0 0 {{ copy.width }} {{ copy.height }}">
252
<rect x="{{ region.json.shape.x * copy.width }}"
253
y="{{ region.json.shape.y * copy.height }}"
254
width="{{ region.json.shape.w * copy.width }}"
255
height="{{ region.json.shape.h * copy.height }}"
256
fill="none" class="shape-bbox shape"
257
></rect>
258
</svg>
259
{% elif region.json.type == "polygon" %}
260
<svg class="shape-container" viewBox="0 0 {{ copy.width }} {{ copy.height }}">
261
<polygon points="{% for point in region.json.shape %}{{ point.x * copy.width }},{{ point.y * copy.height }} {% endfor %}" fill="none" class="shape-polygon shape"></polygon>
262
</svg>
263
{% elif region.json.type == "polyline" %}
264
<svg class="shape-container" viewBox="0 0 {{ copy.width }} {{ copy.height }}">
265
<polyline points="{% for point in region.json.shape %}{{ point.x * copy.width }},{{ point.y * copy.height }} {% endfor %}" fill="none" class="shape-polyline shape"></polyline>
266
</svg>
267
{% elif region.json.type == "point" %}
268
<svg class="shape-container" viewBox="0 0 {{ copy.width }} {{ copy.height }}">
269
<circle cx="{{ region.json.shape.x * copy.width }}" cy="{{ region.json.shape.y * copy.height }}" r="0" fill="none" class="shape-point shape"></circle>
270
</svg>
271
{% endif %}
272
{% endfor %}
273
</div>
274
<div class="list-detail">
275
{{ copy.title }}
276
</div>
277
</a>
278
<div class="list-more">
279
<form method="POST" action="/picture/{{ copy.id }}/mark-replacement">
280
<button type="submit">Designate replacement</button>
281
</form>
282
</div>
283
</li>
284
{% endfor %}
285
</ul>
286
{% else %}
287
<p>This picture hasn't got any copies.</p>
288
{% endif %}
289
290
<h2>Galleries</h2>
291
{% if resource.galleries %}
292
<ul class="thumbnail-list">
293
{% for gallery in resource.galleries %}
294
<li>
295
<a href="/gallery/{{ gallery.gallery.id }}">
296
<div class="list-detail">
297
{{ gallery.gallery.title }}
298
</div>
299
</a>
300
</li>
301
{% endfor %}
302
</ul>
303
{% else %}
304
<p>This picture isn't in any galleries.</p>
305
{% endif %}
306
</x-vbox>
307
</x-frame>
308
</div>
309
<svg width="0" height="0">
310
<defs>
311
<clipPath id="star-clip">
312
<path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.62L12 2L9.19 8.62L2 9.24l5.45 4.73L5.82 21z"></path>
313
</clipPath>
314
</defs>
315
</svg>
316
{% endblock %}