girder-large-image 1.23.3.dev31__tar.gz → 1.23.3.dev59__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/PKG-INFO +1 -1
  2. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/rest/tiles.py +70 -4
  3. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/views/imageViewerSelectWidget.js +8 -2
  4. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/vue/components/CompositeLayers.vue +30 -20
  5. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/vue/components/FrameSelector.vue +6 -2
  6. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/vue/components/PresetsMenu.vue +76 -35
  7. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image.egg-info/PKG-INFO +1 -1
  8. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image.egg-info/requires.txt +2 -2
  9. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/test_girder/test_tiles_rest.py +27 -0
  10. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/LICENSE +0 -0
  11. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/README.rst +0 -0
  12. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/__init__.py +0 -0
  13. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/constants.py +0 -0
  14. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/girder_tilesource.py +0 -0
  15. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/loadmodelcache.py +0 -0
  16. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/models/__init__.py +0 -0
  17. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/models/image_item.py +0 -0
  18. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/rest/__init__.py +0 -0
  19. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/rest/item_meta.py +0 -0
  20. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/rest/large_image_resource.py +0 -0
  21. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/eventStream.js +0 -0
  22. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/extra/.gitignore +0 -0
  23. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/index.js +0 -0
  24. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/main.js +0 -0
  25. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/package.json +0 -0
  26. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/routes.js +0 -0
  27. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/stylesheets/fileList.styl +0 -0
  28. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/stylesheets/imageViewerSelectWidget.styl +0 -0
  29. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/stylesheets/itemList.styl +0 -0
  30. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/stylesheets/itemView.styl +0 -0
  31. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/stylesheets/itemViewCodemirror.styl +0 -0
  32. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/stylesheets/largeImageConfig.styl +0 -0
  33. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/stylesheets/metadataWidget.styl +0 -0
  34. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/templates/imageViewerSelectWidget.pug +0 -0
  35. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/templates/itemList.pug +0 -0
  36. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/templates/itemView.pug +0 -0
  37. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/templates/itemViewCodemirror.pug +0 -0
  38. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/templates/largeImageConfig.pug +0 -0
  39. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/templates/largeImage_fileAction.pug +0 -0
  40. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/templates/metadataWidget.pug +0 -0
  41. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/templates/metadatumEditWidget.pug +0 -0
  42. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/views/configView.js +0 -0
  43. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/views/fileList.js +0 -0
  44. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/views/imageViewerWidget/base.js +0 -0
  45. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/views/imageViewerWidget/geojs.js +0 -0
  46. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/views/imageViewerWidget/index.js +0 -0
  47. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/views/imageViewerWidget/leaflet.js +0 -0
  48. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/views/imageViewerWidget/openlayers.js +0 -0
  49. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/views/imageViewerWidget/openseadragon.js +0 -0
  50. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/views/imageViewerWidget/setFrameQuad.js +0 -0
  51. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/views/imageViewerWidget/slideatlas.js +0 -0
  52. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/views/index.js +0 -0
  53. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/views/itemList.js +0 -0
  54. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/views/itemView.js +0 -0
  55. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/views/itemViewCodemirror.js +0 -0
  56. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/views/itemViewWidget.js +0 -0
  57. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/views/metadataWidget.js +0 -0
  58. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/vue/components/DualInput.vue +0 -0
  59. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/vue/components/HistogramEditor.vue +0 -0
  60. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/vue/utils/clamp.js +0 -0
  61. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/vue/utils/colors.js +0 -0
  62. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/vue/utils/drag.js +0 -0
  63. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image/web_client/webpack.helper.js +0 -0
  64. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image.egg-info/SOURCES.txt +0 -0
  65. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image.egg-info/dependency_links.txt +0 -0
  66. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image.egg-info/entry_points.txt +0 -0
  67. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image.egg-info/not-zip-safe +0 -0
  68. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/girder_large_image.egg-info/top_level.txt +0 -0
  69. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/setup.cfg +0 -0
  70. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/setup.py +0 -0
  71. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/test_girder/__init__.py +0 -0
  72. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/test_girder/conftest.py +0 -0
  73. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/test_girder/girder_utilities.py +0 -0
  74. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/test_girder/test_large_image.py +0 -0
  75. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/test_girder/test_web_client.py +0 -0
  76. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/test_girder/web_client_specs/.eslintrc +0 -0
  77. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/test_girder/web_client_specs/imageViewerSpec.js +0 -0
  78. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/test_girder/web_client_specs/largeImageSpec.js +0 -0
  79. {girder-large-image-1.23.3.dev31 → girder-large-image-1.23.3.dev59}/test_girder/web_client_specs/otherFeatures.js +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: girder-large-image
3
- Version: 1.23.3.dev31
3
+ Version: 1.23.3.dev59
4
4
  Summary: A Girder plugin to work with large, multiresolution images.
5
5
  Home-page: https://github.com/girder/large_image
6
6
  Author: Kitware, Inc.
@@ -15,6 +15,7 @@
15
15
  #############################################################################
16
16
 
17
17
  import hashlib
18
+ import io
18
19
  import math
19
20
  import os
20
21
  import pathlib
@@ -33,6 +34,7 @@ from girder.constants import AccessType, TokenScope
33
34
  from girder.exceptions import RestException
34
35
  from girder.models.file import File
35
36
  from girder.models.item import Item
37
+ from girder.models.upload import Upload
36
38
  from girder.utility.progress import setResponseTimeLimit
37
39
  from large_image.cache_util import strhash
38
40
  from large_image.constants import TileInputUnits, TileOutputMimeTypes
@@ -150,6 +152,7 @@ class TilesItemResource(ItemResource):
150
152
  apiRoot.item.route('GET', (':itemId', 'tiles', 'thumbnail'), self.getTilesThumbnail)
151
153
  apiRoot.item.route('GET', (':itemId', 'tiles', 'thumbnails'), self.listTilesThumbnails)
152
154
  apiRoot.item.route('DELETE', (':itemId', 'tiles', 'thumbnails'), self.deleteTilesThumbnails)
155
+ apiRoot.item.route('POST', (':itemId', 'tiles', 'thumbnails'), self.addTilesThumbnails)
153
156
  apiRoot.item.route('GET', (':itemId', 'tiles', 'region'), self.getTilesRegion)
154
157
  apiRoot.item.route('GET', (':itemId', 'tiles', 'tile_frames'), self.tileFrames)
155
158
  apiRoot.item.route('GET', (':itemId', 'tiles', 'tile_frames', 'quad_info'),
@@ -1429,11 +1432,74 @@ class TilesItemResource(ItemResource):
1429
1432
  @autoDescribeRoute(
1430
1433
  Description('Delete thumbnail and data files associated with a large_image item.')
1431
1434
  .modelParam('itemId', model=Item, level=AccessType.READ)
1432
- .param('keep', 'Number of thumbnails to keep.', dataType='integer',
1433
- required=False, default=10000)
1435
+ .param('keep', 'Number of thumbnails to keep. Ignored if a key is '
1436
+ 'specified.', dataType='integer', required=False,
1437
+ default=10000)
1438
+ .param('key', 'A specific key to delete', required=False)
1439
+ .param('thumbnail', 'If a key is specified, true if the key is a '
1440
+ 'thumbnail; false if the key is a data record',
1441
+ dataType='boolean', required=False)
1434
1442
  .errorResponse('ID was invalid.')
1435
1443
  .errorResponse('Read access was denied for the item.', 403)
1436
1444
  )
1437
1445
  @access.admin(scope=TokenScope.DATA_WRITE)
1438
- def deleteTilesThumbnails(self, item, keep):
1439
- return self.imageItemModel.removeThumbnailFiles(item, keep=keep or 0)
1446
+ def deleteTilesThumbnails(self, item, keep, key=None, thumbnail=True):
1447
+ if not key:
1448
+ return self.imageItemModel.removeThumbnailFiles(item, keep=keep or 0)
1449
+ thumbnail = str(thumbnail).lower() != 'false'
1450
+ query = {
1451
+ 'attachedToType': 'item',
1452
+ 'attachedToId': item['_id'],
1453
+ 'isLargeImageThumbnail' if thumbnail is not False else 'isLargeImageData': True,
1454
+ 'thumbnailKey': key,
1455
+ }
1456
+ file = File().findOne(query)
1457
+ if file:
1458
+ File().remove(file)
1459
+ return [file]
1460
+
1461
+ @autoDescribeRoute(
1462
+ Description('Associate or replace a thumbnail or data file with a large_image items.')
1463
+ .responseClass('File')
1464
+ .modelParam('itemId', model=Item, level=AccessType.WRITE)
1465
+ .param('key', 'A specific key to delete', required=True)
1466
+ .param('thumbnail', 'If a key is specified, true if the key is a '
1467
+ 'thumbnail; false if the key is a data record',
1468
+ dataType='boolean', required=False)
1469
+ .param('mimeType', 'The MIME type of the file.', required=False)
1470
+ .param('data', 'An image or data block to associated with the large_image item.',
1471
+ paramType='body', dataType='binary')
1472
+ .consumes('application/octet-stream')
1473
+ .errorResponse('ID was invalid.')
1474
+ .errorResponse('Read access was denied for the item.', 403)
1475
+ )
1476
+ @access.user(scope=TokenScope.DATA_WRITE)
1477
+ def addTilesThumbnails(self, item, key, mimeType, thumbnail=False, data=None):
1478
+ user = self.getCurrentUser()
1479
+ thumbnail = str(thumbnail).lower() != 'false'
1480
+ query = {
1481
+ 'attachedToType': 'item',
1482
+ 'attachedToId': item['_id'],
1483
+ 'isLargeImageThumbnail' if thumbnail is not False else 'isLargeImageData': True,
1484
+ 'thumbnailKey': key,
1485
+ }
1486
+ file = File().findOne(query)
1487
+ if file:
1488
+ File().remove(file)
1489
+ data = cherrypy.request.body.read()
1490
+ try:
1491
+ import magic
1492
+ mimeType = magic.from_buffer(data, mime=True) or mimeType
1493
+ except Exception:
1494
+ pass
1495
+ mimeType = mimeType or 'application/octet-stream'
1496
+ datafile = Upload().uploadFromFile(
1497
+ io.BytesIO(data), size=len(data),
1498
+ name='_largeImageThumbnail', parentType='item', parent=item,
1499
+ user=user, mimeType=mimeType, attachParent=True)
1500
+ datafile.update({
1501
+ 'isLargeImageThumbnail' if thumbnail is not False else 'isLargeImageData': True,
1502
+ 'thumbnailKey': key,
1503
+ })
1504
+ datafile = File().save(datafile)
1505
+ return datafile
@@ -47,7 +47,12 @@ var ImageViewerSelectWidget = View.extend({
47
47
  this.itemId = settings.imageModel.id;
48
48
  this.model = settings.imageModel;
49
49
  this.currentViewer = null;
50
- largeImageConfig.getSettings(() => this.render());
50
+ largeImageConfig.getSettings(() => {
51
+ largeImageConfig.getConfigFile(this.model.get('folderId')).done((config) => {
52
+ this._liConfig = config || {};
53
+ this.render();
54
+ });
55
+ });
51
56
  },
52
57
 
53
58
  render: function () {
@@ -73,7 +78,8 @@ var ImageViewerSelectWidget = View.extend({
73
78
  propsData: {
74
79
  itemId: this.itemId,
75
80
  imageMetadata: imageMetadata,
76
- frameUpdate: frameUpdate
81
+ frameUpdate: frameUpdate,
82
+ liConfig: this._liConfig
77
83
  }
78
84
  });
79
85
  this.vueApp = vm;
@@ -66,7 +66,6 @@ export default {
66
66
  enabled: true,
67
67
  min: undefined,
68
68
  max: undefined,
69
- custom: false,
70
69
  autoRange: undefined
71
70
  }
72
71
  })
@@ -102,36 +101,45 @@ export default {
102
101
  },
103
102
  initializeStateFromStyle() {
104
103
  this.enabledLayers = []
105
- const styleArray = this.currentStyle.bands
106
- this.layers.forEach((layerName) => {
107
- const layerInfo = this.compositeLayerInfo[layerName]
108
- const currentLayerStyle = styleArray.find((s) => s.framedelta === layerInfo.framedelta && s.band === layerInfo.band)
104
+ this.layers.forEach((layer) => {
105
+ const layerInfo = this.compositeLayerInfo[layer]
106
+ const currentLayerStyle = this.currentStyle.bands.find(
107
+ (s) => s.framedelta === layerInfo.framedelta
108
+ && s.band === layerInfo.band
109
+ )
109
110
  if (currentLayerStyle) {
110
- this.enabledLayers.push(layerName)
111
+ this.enabledLayers.push(layer)
112
+ this.compositeLayerInfo[layer].enabled = true;
113
+ this.compositeLayerInfo[layer].palette = currentLayerStyle.palette;
111
114
  if (
112
115
  currentLayerStyle.min && currentLayerStyle.max
113
- && currentLayerStyle.min.includes("min:")
114
- && currentLayerStyle.max.includes("max:")
116
+ && currentLayerStyle.min.toString().includes("min:")
117
+ && currentLayerStyle.max.toString().includes("max:")
115
118
  ) {
116
- currentLayerStyle.autoRange = parseFloat(
117
- currentLayerStyle.min.replace("min:", '')
119
+ this.compositeLayerInfo[layer].autoRange = parseFloat(
120
+ currentLayerStyle.min.toString().replace("min:", '')
118
121
  ) * 100
119
- currentLayerStyle.min = undefined
120
- currentLayerStyle.max = undefined
122
+ this.compositeLayerInfo[layer].min = undefined
123
+ this.compositeLayerInfo[layer].max = undefined
124
+ } else {
125
+ this.compositeLayerInfo[layer].autoRange = undefined
121
126
  }
122
127
  }
123
- this.compositeLayerInfo[layerName] = Object.assign(
124
- {}, layerInfo, currentLayerStyle
125
- )
126
- })
127
- this.layers.forEach((layer) => {
128
- this.compositeLayerInfo[layer].enabled = this.enabledLayers.includes(layer);
128
+ else {
129
+ this.compositeLayerInfo[layer].enabled = false;
130
+ this.compositeLayerInfo[layer].autoRange = undefined;
131
+ this.compositeLayerInfo[layer].min = undefined;
132
+ this.compositeLayerInfo[layer].max = undefined;
133
+ }
129
134
  })
135
+
130
136
  const autoRanges = Object.entries(this.compositeLayerInfo)
137
+ .filter(([index, info]) => info.enabled)
131
138
  .map(([index, info]) => info.autoRange)
132
- .filter((a) => a !== undefined)
133
139
  if (autoRanges.every((v) => v === autoRanges[0])) {
134
140
  this.autoRangeForAll = autoRanges[0]
141
+ } else {
142
+ this.autoRangeForAll = undefined
135
143
  }
136
144
  },
137
145
  fetchCurrentFrameHistogram() {
@@ -285,7 +293,9 @@ export default {
285
293
  }
286
294
  },
287
295
  currentStyle() {
288
- this.initializeStateFromStyle()
296
+ if(this.currentStyle.preset) {
297
+ this.initializeStateFromStyle()
298
+ }
289
299
  }
290
300
  }
291
301
  }
@@ -5,7 +5,7 @@ import DualInput from './DualInput.vue';
5
5
  import PresetsMenu from './PresetsMenu.vue';
6
6
 
7
7
  export default Vue.extend({
8
- props: ['itemId', 'imageMetadata', 'frameUpdate'],
8
+ props: ['itemId', 'imageMetadata', 'frameUpdate', 'liConfig'],
9
9
  components: { CompositeLayers, DualInput, PresetsMenu },
10
10
  data() {
11
11
  return {
@@ -75,7 +75,8 @@ export default Vue.extend({
75
75
  }
76
76
  });
77
77
  this.currentFrame = frame
78
- let style = this.currentModeId > 1 ? this.style[this.currentModeId] : undefined
78
+ let style = this.currentModeId > 1 ? Object.assign({}, this.style[this.currentModeId]) : undefined
79
+ if(style && style.preset) delete style.preset
79
80
  this.frameUpdate(frame, style);
80
81
  },
81
82
  fillMetadata() {
@@ -200,6 +201,9 @@ export default Vue.extend({
200
201
  </select>
201
202
  <presets-menu
202
203
  :itemId="itemId"
204
+ :liConfig="liConfig"
205
+ :imageMetadata="imageMetadata"
206
+ :availableModes="sliderModes.map((m) => m.id)"
203
207
  :currentMode="sliderModes.find((m) => m.id === currentModeId)"
204
208
  :currentFrame="currentFrame"
205
209
  :currentStyle="style[currentModeId]"
@@ -2,11 +2,12 @@
2
2
  import { restRequest } from '@girder/core/rest';
3
3
 
4
4
  export default {
5
- props: ['itemId', 'currentMode', 'currentFrame', 'currentStyle'],
5
+ props: ['itemId', 'liConfig', 'imageMetadata', 'availableModes', 'currentMode', 'currentFrame', 'currentStyle'],
6
6
  emits: ['setCurrentMode', 'setCurrentFrame', 'updateStyle'],
7
7
  data() {
8
8
  return {
9
- availablePresets: [],
9
+ itemPresets: [],
10
+ folderPresets: [],
10
11
  selectedPreset: undefined,
11
12
  showPresetCreation: false,
12
13
  newPresetName: undefined,
@@ -14,13 +15,36 @@ export default {
14
15
  }
15
16
  },
16
17
  methods: {
18
+ presetApplicable(preset) {
19
+ if (this.itemPresets.find((p) => p.name === preset.name)) {
20
+ // preset with this name already exists on the item,
21
+ // prefer the item preset; don't show both
22
+ return false
23
+ } else if (
24
+ parseInt(preset.frame) >= this.imageMetadata.frames.length
25
+ || !this.availableModes.includes(preset.mode.id)
26
+ ) {
27
+ return false
28
+ } else if (
29
+ preset.style && preset.style.bands
30
+ && this.imageMetadata.IndexRange.IndexC
31
+ && preset.style.bands.some((b) => b.framedelta > this.imageMetadata.IndexRange.IndexC)
32
+ ) {
33
+ return false
34
+ }
35
+
36
+ return true
37
+ },
17
38
  getPresets() {
18
39
  restRequest({
19
40
  type: 'GET',
20
41
  url: 'item/' + this.itemId + '/internal_metadata/presets',
21
42
  }).then((presets) => {
22
43
  if (presets) {
23
- this.availablePresets = presets
44
+ this.itemPresets = presets
45
+ }
46
+ if (this.liConfig.imageFramePresets) {
47
+ this.folderPresets = this.liConfig.imageFramePresets.filter(this.presetApplicable)
24
48
  }
25
49
  })
26
50
  },
@@ -35,8 +59,8 @@ export default {
35
59
  if (!overwrite && this.availablePresets.find((p) => p.name === newPreset.name)) {
36
60
  this.errorMessage = `There is already a preset named "${newPreset.name}". Overwrite "${newPreset.name}"?`
37
61
  } else {
38
- this.availablePresets = this.availablePresets.filter((p) => p.name !== newPreset.name)
39
- this.availablePresets.push(newPreset)
62
+ this.itemPresets = this.itemPresets.filter((p) => p.name !== newPreset.name)
63
+ this.itemPresets.push(newPreset)
40
64
  this.selectedPreset = newPreset.name
41
65
  this.savePresetsList()
42
66
  this.newPresetName = undefined
@@ -45,7 +69,7 @@ export default {
45
69
  }
46
70
  },
47
71
  deleteSelectedPreset() {
48
- this.availablePresets = this.availablePresets.filter((p) => p.name !== this.selectedPreset)
72
+ this.itemPresets = this.itemPresets.filter((p) => p.name !== this.selectedPreset)
49
73
  this.selectedPreset = undefined
50
74
  this.savePresetsList()
51
75
  },
@@ -53,47 +77,48 @@ export default {
53
77
  restRequest({
54
78
  type: 'PUT',
55
79
  url: 'item/' + this.itemId + '/internal_metadata/presets',
56
- data: JSON.stringify(this.availablePresets),
80
+ data: JSON.stringify(this.itemPresets),
57
81
  contentType: 'application/json',
58
82
  })
59
83
  },
84
+ styleFromAutoRange(band) {
85
+ band = Object.assign({}, band) // new reference
86
+ if (band.autoRange) {
87
+ band.min = `min:${band.autoRange / 100}`
88
+ band.max = `max:${band.autoRange / 100}`
89
+ delete band.autoRange
90
+ }
91
+ return band
92
+ },
60
93
  styleEqual(style1, style2) {
61
94
  if (style1 === style2) {
62
95
  return true
63
96
  }
64
- if (style1.bands.length !== style2.bands.length) {
97
+ if (style1.length !== style2.length) {
65
98
  return false
66
99
  }
67
- return style1.bands.every((b1) => {
68
- b1 = Object.fromEntries(Object.entries(b1).filter(([k, v]) => v !== undefined))
69
- return style2.bands.some((b2) => {
70
- b2 = Object.fromEntries(Object.entries(b2).filter(([k, v]) => v !== undefined))
100
+ return style1.every((b1) => {
101
+ b1 = this.styleFromAutoRange(b1)
102
+ let b2 = style2.find((b) => b.framedelta === b1.framedelta && b.band === b1.band)
103
+ if (b2) {
104
+ b2 = this.styleFromAutoRange(b2)
71
105
  return (
72
- Object.entries(b1).every(([k, v]) => b2[k] === v)
73
- && Object.entries(b2).every(([k, v]) => b1[k] === v)
106
+ b1.min === b2.min
107
+ && b1.max === b2.max
108
+ && b1.palette === b2.palette
74
109
  )
75
- })
110
+ } else return false
76
111
  })
77
112
  },
78
113
  checkPresetMatch() {
79
- const targetStyle = this.currentStyle && this.currentStyle.bands ? {
80
- bands: this.currentStyle.bands.map((b) => {
81
- if (b.min && b.max && b.min.includes("min:") && b.max.includes("max:")) {
82
- b.autoRange = parseFloat(
83
- b.min.replace("min:", '')
84
- ) * 100
85
- b.min = undefined
86
- b.max = undefined
87
- }
88
- return b
89
- })
90
- } : this.currentStyle
91
- const match = this.availablePresets.find((p) => (
92
- p.mode.id === this.currentMode.id
93
- && p.frame === this.currentFrame
94
- && this.styleEqual(targetStyle, p.style)
95
- ))
96
- this.selectedPreset = match ? match.name : undefined
114
+ if (this.currentStyle) {
115
+ const match = this.availablePresets.find((p) => (
116
+ p.mode.id === this.currentMode.id
117
+ && p.frame === this.currentFrame
118
+ && this.styleEqual(this.currentStyle.bands, p.style.bands)
119
+ ))
120
+ this.selectedPreset = match ? match.name : undefined
121
+ }
97
122
  }
98
123
  },
99
124
  computed: {
@@ -105,6 +130,9 @@ export default {
105
130
  name = `${this.currentStyle.bands.length} bands`
106
131
  }
107
132
  return name;
133
+ },
134
+ availablePresets() {
135
+ return this.itemPresets.concat(this.folderPresets)
108
136
  }
109
137
  },
110
138
  watch: {
@@ -122,8 +150,21 @@ export default {
122
150
  if (preset.frame !== undefined) {
123
151
  this.$emit('setCurrentFrame', preset.frame)
124
152
  }
125
- if (preset.style && Object.keys(preset.style.bands).length) {
126
- this.$emit('updateStyle', preset.mode.id, preset.style)
153
+ if (preset.style && preset.style.bands.length) {
154
+ const styleArray = []
155
+ preset.style.bands.forEach((layer) => {
156
+ const styleEntry = {
157
+ min: layer.autoRange !== undefined ? `min:${layer.autoRange / 100}` : parseInt(layer.min),
158
+ max: layer.autoRange !== undefined ? `max:${layer.autoRange / 100}` : parseInt(layer.max),
159
+ palette: layer.palette,
160
+ framedelta: layer.framedelta,
161
+ band: layer.band,
162
+ }
163
+ if (!styleEntry.min) delete styleEntry.min
164
+ if (!styleEntry.max) delete styleEntry.max
165
+ styleArray.push(styleEntry);
166
+ });
167
+ this.$emit('updateStyle', preset.mode.id, {bands: styleArray, preset: true})
127
168
  }
128
169
  }
129
170
  },
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: girder-large-image
3
- Version: 1.23.3.dev31
3
+ Version: 1.23.3.dev59
4
4
  Summary: A Girder plugin to work with large, multiresolution images.
5
5
  Home-page: https://github.com/girder/large_image
6
6
  Author: Kitware, Inc.
@@ -1,10 +1,10 @@
1
1
  girder>=3.1.18
2
2
  girder-jobs>=3.0.3
3
- large_image>=1.23.3.dev31
3
+ large_image>=1.23.3.dev59
4
4
 
5
5
  [:python_version < "3.8"]
6
6
  importlib-metadata<5
7
7
 
8
8
  [tasks]
9
- large-image-tasks[girder]>=1.23.3.dev31
9
+ large-image-tasks[girder]>=1.23.3.dev59
10
10
  girder-worker[girder]>=0.6.0
@@ -1499,3 +1499,30 @@ def testThumbnailMaintenance(server, admin, fsAssetstore):
1499
1499
  resp = server.request(path='/item/%s/tiles/thumbnails' % itemId, user=admin)
1500
1500
  assert utilities.respStatus(resp) == 200
1501
1501
  assert len(resp.json) == 0
1502
+
1503
+ # Get a thumbnail
1504
+ resp = server.request(path='/item/%s/tiles/thumbnail' % itemId,
1505
+ user=admin, isJson=False)
1506
+ assert utilities.respStatus(resp) == 200
1507
+ thumb = utilities.getBody(resp, text=False)
1508
+ resp = server.request(path='/item/%s/tiles/thumbnails' % itemId, user=admin)
1509
+ assert utilities.respStatus(resp) == 200
1510
+ # Ask to delete it specifically
1511
+ key = resp.json[0]['thumbnailKey']
1512
+ resp = server.request(
1513
+ path='/item/%s/tiles/thumbnails' % itemId, method='DELETE', user=admin,
1514
+ params={'key': key})
1515
+ assert utilities.respStatus(resp) == 200
1516
+ assert len(resp.json) == 1
1517
+ # It should be gone
1518
+ resp = server.request(path='/item/%s/tiles/thumbnails' % itemId, user=admin)
1519
+ assert utilities.respStatus(resp) == 200
1520
+ assert len(resp.json) == 0
1521
+ # Add it back
1522
+ resp = server.request(
1523
+ path='/item/%s/tiles/thumbnails' % itemId, method='POST', user=admin,
1524
+ params={'key': key}, body=thumb, type='application/octet-stream')
1525
+
1526
+ resp = server.request(path='/item/%s/tiles/thumbnails' % itemId, user=admin)
1527
+ assert utilities.respStatus(resp) == 200
1528
+ assert len(resp.json) == 1