diff --git a/MIGRATIONS.unreleased.md b/MIGRATIONS.unreleased.md index 23c145bced4..7c8b740c233 100644 --- a/MIGRATIONS.unreleased.md +++ b/MIGRATIONS.unreleased.md @@ -12,3 +12,4 @@ User-facing changes are documented in the [changelog](CHANGELOG.released.md). - [131-more-indices-on-users.sql](conf/evolutions/131-more-indices-on-users.sql) - [132-remove-stored-meshes.sql](conf/evolutions/132-remove-stored-meshes.sql) +- [133-datasource-properties-in-db.sql](conf/evolutions/133-datasource-properties-in-db.sql) diff --git a/app/models/dataset/Dataset.scala b/app/models/dataset/Dataset.scala index c8b20fbef85..8f24214483d 100755 --- a/app/models/dataset/Dataset.scala +++ b/app/models/dataset/Dataset.scala @@ -750,12 +750,31 @@ class DatasetMagsDAO @Inject()(sqlClient: SqlClient)(implicit ec: ExecutionConte def updateMags(datasetId: ObjectId, dataLayersOpt: Option[List[DataLayer]]): Fox[Unit] = { val clearQuery = q"DELETE FROM webknossos.dataset_mags WHERE _dataset = $datasetId".asUpdate val insertQueries = dataLayersOpt.getOrElse(List.empty).flatMap { layer: DataLayer => - layer.resolutions.distinct.map { mag: Vec3Int => - { - q"""INSERT INTO webknossos.dataset_mags(_dataset, dataLayerName, mag) - VALUES($datasetId, ${layer.name}, $mag)""".asUpdate - } + layer.magsOpt match { + case Some(mags) => + mags.map(mag => { + q"""INSERT INTO webknossos.dataset_mags(_dataset, dataLayerName, mag, axisOrder, channelIndex, credentialId) + VALUES($datasetId, ${layer.name}, ${mag.mag}, ${mag.axisOrder + .map(Json.toJson(_))}, ${mag.channelIndex}, ${mag.credentialId}) + """.asUpdate + }) + case None => + layer.wkwResolutionsOpt match { + case Some(resolutions) => + resolutions.map(wkwResolution => { + q"""INSERT INTO webknossos.dataset_mags(_dataset, dataLayerName, mag, cubeLength) + VALUES ($datasetId, ${layer.name}, ${wkwResolution.resolution}, ${wkwResolution.cubeLength})""".asUpdate + }) + case None => + layer.resolutions.distinct.map { mag: Vec3Int => + { + q"""INSERT INTO webknossos.dataset_mags(_dataset, dataLayerName, mag) + VALUES($datasetId, ${layer.name}, $mag)""".asUpdate + } + } + } } + } replaceSequentiallyAsTransaction(clearQuery, insertQueries) } @@ -897,11 +916,12 @@ class DatasetLayerDAO @Inject()( layer match { case s: AbstractSegmentationLayer => val mappings = s.mappings.getOrElse(Set()).toList - q"""INSERT INTO webknossos.dataset_layers(_dataset, name, category, elementClass, boundingBox, largestSegmentId, mappings, defaultViewConfiguration, adminViewConfiguration) + q"""INSERT INTO webknossos.dataset_layers(_dataset, name, category, elementClass, boundingBox, largestSegmentId, mappings, defaultViewConfiguration, adminViewConfiguration, dataFormat, numChannels) VALUES($datasetId, ${s.name}, ${s.category}, ${s.elementClass}, ${s.boundingBox}, ${s.largestSegmentId}, $mappings, ${s.defaultViewConfiguration.map(Json.toJson(_))}, - ${s.adminViewConfiguration.map(Json.toJson(_))}) + ${s.adminViewConfiguration.map(Json.toJson(_))}, + ${s.dataFormat}, ${s.numChannels}) ON CONFLICT (_dataset, name) DO UPDATE SET category = ${s.category}, @@ -909,19 +929,26 @@ class DatasetLayerDAO @Inject()( boundingBox = ${s.boundingBox}, largestSegmentId = ${s.largestSegmentId}, mappings = $mappings, - defaultViewConfiguration = ${s.defaultViewConfiguration.map(Json.toJson(_))}""".asUpdate + defaultViewConfiguration = ${s.defaultViewConfiguration.map(Json.toJson(_))}, + adminViewConfiguration = ${s.adminViewConfiguration.map(Json.toJson(_))}, + numChannels = ${s.numChannels}, + dataFormat = ${s.dataFormat} """.asUpdate case d: AbstractDataLayer => - q"""INSERT INTO webknossos.dataset_layers(_dataset, name, category, elementClass, boundingBox, defaultViewConfiguration, adminViewConfiguration) + q"""INSERT INTO webknossos.dataset_layers(_dataset, name, category, elementClass, boundingBox, defaultViewConfiguration, adminViewConfiguration, dataFormat, numChannels) VALUES($datasetId, ${d.name}, ${d.category}, ${d.elementClass}, ${d.boundingBox}, ${d.defaultViewConfiguration.map(Json.toJson(_))}, - ${d.adminViewConfiguration.map(Json.toJson(_))}) + ${d.adminViewConfiguration.map(Json.toJson(_))}, + ${d.dataFormat}, ${d.numChannels}) ON CONFLICT (_dataset, name) DO UPDATE SET category = ${d.category}, elementClass = ${d.elementClass}, boundingBox = ${d.boundingBox}, - defaultViewConfiguration = ${d.defaultViewConfiguration.map(Json.toJson(_))}""".asUpdate + defaultViewConfiguration = ${d.defaultViewConfiguration.map(Json.toJson(_))}, + adminViewConfiguration = ${d.adminViewConfiguration.map(Json.toJson(_))}, + numChannels = ${d.numChannels}, + dataFormat = ${d.dataFormat}""".asUpdate case _ => throw new Exception("DataLayer type mismatch") } diff --git a/conf/evolutions/133-datasource-properties-in-db.sql b/conf/evolutions/133-datasource-properties-in-db.sql new file mode 100644 index 00000000000..fdb88038740 --- /dev/null +++ b/conf/evolutions/133-datasource-properties-in-db.sql @@ -0,0 +1,23 @@ +START TRANSACTION; + +do $$ begin ASSERT (select schemaVersion from webknossos.releaseInformation) = 132, 'Previous schema version mismatch'; end; $$ LANGUAGE plpgsql; + +-- The aim of this migration is to have all properties of datasources that are saved in the datasource-properties.json +-- file saved in the database. + +CREATE TYPE webknossos.DATASET_LAYER_DATAFORMAT AS ENUM ('wkw','zarr','zarr3','n5','neuroglancerPrecomputed'); + +ALTER TABLE webknossos.dataset_layers + ADD COLUMN IF NOT EXISTS numChannels INT, + ADD COLUMN IF NOT EXISTS dataFormat webknossos.DATASET_LAYER_DATAFORMAT; + +ALTER TABLE webknossos.dataset_mags + ADD COLUMN IF NOT EXISTS credentialId TEXT, + ADD COLUMN IF NOT EXISTS axisOrder JSONB CONSTRAINT axisOrder_requiredKeys CHECK (axisOrder ? 'x' AND axisOrder ? 'y'), + ADD COLUMN IF NOT EXISTS channelIndex INT, + ADD COLUMN IF NOT EXISTS cubeLength INT; + -- legacy credentials omitted + +UPDATE webknossos.releaseInformation SET schemaVersion = 133; + +COMMIT TRANSACTION; diff --git a/conf/evolutions/reversions/133-datasource-properties-in-db.sql b/conf/evolutions/reversions/133-datasource-properties-in-db.sql new file mode 100644 index 00000000000..8daf1e5e20c --- /dev/null +++ b/conf/evolutions/reversions/133-datasource-properties-in-db.sql @@ -0,0 +1,19 @@ +START TRANSACTION; + +do $$ begin ASSERT (select schemaVersion from webknossos.releaseInformation) = 133, 'Previous schema version mismatch'; end; $$ LANGUAGE plpgsql; + +ALTER TABLE webknossos.dataset_layers + DROP COLUMN IF EXISTS numChannels, + DROP COLUMN IF EXISTS dataFormat; + +DROP TYPE IF EXISTS webknossos.DATASET_LAYER_DATAFORMAT; + +ALTER TABLE webknossos.dataset_mags + DROP COLUMN IF EXISTS credentialId, + DROP COLUMN IF EXISTS axisOrder, + DROP COLUMN IF EXISTS channelIndex, + DROP COLUMN IF EXISTS cubeLength; + +UPDATE webknossos.releaseInformation SET schemaVersion = 132; + +COMMIT TRANSACTION; diff --git a/conf/webknossos.latest.routes b/conf/webknossos.latest.routes index f0f0f1e6afe..b5cdecaa007 100644 --- a/conf/webknossos.latest.routes +++ b/conf/webknossos.latest.routes @@ -17,7 +17,6 @@ POST /maintenances GET /maintenances/:id controllers.MaintenanceController.readOne(id: ObjectId) PUT /maintenances/:id controllers.MaintenanceController.update(id: ObjectId) DELETE /maintenances/:id controllers.MaintenanceController.delete(id: ObjectId) -POST /maintenances controllers.MaintenanceController.create() POST /maintenances/adHoc controllers.MaintenanceController.createAdHocMaintenance() GET /maintenance/revokeExpiredCredits controllers.CreditTransactionController.revokeExpiredCredits() diff --git a/test/db/dataSet_layers.csv b/test/db/dataSet_layers.csv index a9d0ca9d801..ddd448bdc19 100644 --- a/test/db/dataSet_layers.csv +++ b/test/db/dataSet_layers.csv @@ -1,6 +1,6 @@ -_dataSet,name,category,elementClass,boundingBox,largestSegmentId,mappings,defaultViewConfiguration,adminViewConfiguration -'59e9cfbdba632ac2ab8b23b3','color_1','color','uint8','(0,0,0,512,512,256)',,,, -'59e9cfbdba632ac2ab8b23b3','color_2','color','uint8','(0,0,0,512,512,256)',,,, -'59e9cfbdba632ac2ab8b23b3','color_3','color','uint8','(0,0,0,512,512,256)',,,, -'59e9cfbdba632ac2ab8b23b5','color','color','uint8','(3072,3072,512,1024,1024,1024)',,,, -'59e9cfbdba632ac2ab8b23b5','segmentation','segmentation','uint32','(3072,3072,512,1024,1024,1024)',2504697,'{}',, +_dataSet,name,category,elementClass,boundingBox,largestSegmentId,mappings,defaultViewConfiguration,adminViewConfiguration,numChannels,dataFormat +'59e9cfbdba632ac2ab8b23b3','color_1','color','uint8','(0,0,0,512,512,256)',,,,,,wkw +'59e9cfbdba632ac2ab8b23b3','color_2','color','uint8','(0,0,0,512,512,256)',,,,,,wkw +'59e9cfbdba632ac2ab8b23b3','color_3','color','uint8','(0,0,0,512,512,256)',,,,,,wkw +'59e9cfbdba632ac2ab8b23b5','color','color','uint8','(3072,3072,512,1024,1024,1024)',,,,,,wkw +'59e9cfbdba632ac2ab8b23b5','segmentation','segmentation','uint32','(3072,3072,512,1024,1024,1024)',2504697,'{}',,,,wkw diff --git a/test/db/dataSet_mags.csv b/test/db/dataSet_mags.csv index cdd09999ca3..33548c3669b 100644 --- a/test/db/dataSet_mags.csv +++ b/test/db/dataSet_mags.csv @@ -1,26 +1,26 @@ -_dataSet,dataLayerName,mag,scale,path,realPath,hasLocalData -'59e9cfbdba632ac2ab8b23b3','color_1','(1,1,1)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b3','color_1','(2,2,2)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b3','color_1','(4,4,4)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b3','color_1','(8,8,8)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b3','color_1','(16,16,16)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b3','color_2','(1,1,1)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b3','color_2','(2,2,2)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b3','color_2','(4,4,4)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b3','color_2','(8,8,8)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b3','color_2','(16,16,16)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b3','color_3','(1,1,1)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b3','color_3','(2,2,2)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b3','color_3','(4,4,4)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b3','color_3','(8,8,8)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b3','color_3','(16,16,16)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b5','color','(1,1,1)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b5','color','(2,2,1)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b5','color','(4,4,1)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b5','color','(8,8,2)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b5','color','(16,16,4)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b5','segmentation','(1,1,1)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b5','segmentation','(2,2,1)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b5','segmentation','(4,4,1)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b5','segmentation','(8,8,2)','uninitialized','uninitialized',false -'59e9cfbdba632ac2ab8b23b5','segmentation','(16,16,4)','uninitialized','uninitialized',false +_dataSet,dataLayerName,mag,scale,path,realPath,hasLocalData,channelIndex,cubeLength,credentialId +'59e9cfbdba632ac2ab8b23b3','color_1','(1,1,1)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b3','color_1','(2,2,2)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b3','color_1','(4,4,4)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b3','color_1','(8,8,8)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b3','color_1','(16,16,16)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b3','color_2','(1,1,1)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b3','color_2','(2,2,2)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b3','color_2','(4,4,4)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b3','color_2','(8,8,8)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b3','color_2','(16,16,16)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b3','color_3','(1,1,1)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b3','color_3','(2,2,2)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b3','color_3','(4,4,4)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b3','color_3','(8,8,8)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b3','color_3','(16,16,16)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b5','color','(1,1,1)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b5','color','(2,2,1)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b5','color','(4,4,1)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b5','color','(8,8,2)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b5','color','(16,16,4)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b5','segmentation','(1,1,1)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b5','segmentation','(2,2,1)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b5','segmentation','(4,4,1)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b5','segmentation','(8,8,2)','uninitialized','uninitialized',false,,,, +'59e9cfbdba632ac2ab8b23b5','segmentation','(16,16,4)','uninitialized','uninitialized',false,,,, diff --git a/tools/postgres/schema.sql b/tools/postgres/schema.sql index 1fd002c73ed..1a0ed57a1e1 100644 --- a/tools/postgres/schema.sql +++ b/tools/postgres/schema.sql @@ -21,7 +21,7 @@ CREATE TABLE webknossos.releaseInformation ( schemaVersion BIGINT NOT NULL ); -INSERT INTO webknossos.releaseInformation(schemaVersion) values(132); +INSERT INTO webknossos.releaseInformation(schemaVersion) values(133); COMMIT TRANSACTION; @@ -126,6 +126,7 @@ CREATE TABLE webknossos.datasets( CREATE TYPE webknossos.DATASET_LAYER_CATEGORY AS ENUM ('color', 'mask', 'segmentation'); CREATE TYPE webknossos.DATASET_LAYER_ELEMENT_CLASS AS ENUM ('uint8', 'uint16', 'uint24', 'uint32', 'uint64', 'float', 'double', 'int8', 'int16', 'int32', 'int64'); +CREATE TYPE webknossos.DATASET_LAYER_DATAFORMAT AS ENUM ('wkw','zarr','zarr3','n5','neuroglancerPrecomputed'); CREATE TABLE webknossos.dataset_layers( _dataset TEXT CONSTRAINT _dataset_objectId CHECK (_dataset ~ '^[0-9a-f]{24}$') NOT NULL, name TEXT NOT NULL, @@ -136,6 +137,8 @@ CREATE TABLE webknossos.dataset_layers( mappings TEXT[], defaultViewConfiguration JSONB, adminViewConfiguration JSONB, + numChannels INT, + dataFormat webknossos.DATASET_LAYER_DATAFORMAT, PRIMARY KEY(_dataset, name), CONSTRAINT defaultViewConfigurationIsJsonObject CHECK(jsonb_typeof(defaultViewConfiguration) = 'object'), CONSTRAINT adminViewConfigurationIsJsonObject CHECK(jsonb_typeof(adminViewConfiguration) = 'object') @@ -172,6 +175,10 @@ CREATE TABLE webknossos.dataset_mags( path TEXT, realPath TEXT, hasLocalData BOOLEAN NOT NULL DEFAULT FALSE, + axisOrder JSONB CONSTRAINT axisOrder_requiredKeys CHECK (axisOrder ? 'x' AND axisOrder ? 'y'), + channelIndex INT, + cubeLength INT, + credentialId TEXT, PRIMARY KEY (_dataset, dataLayerName, mag) ); diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/AxisOrder.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/AxisOrder.scala index 1dda3271229..809d4d5e5cf 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/AxisOrder.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/AxisOrder.scala @@ -20,7 +20,6 @@ case class AxisOrder(x: Int, y: Int, z: Option[Int], c: Option[Int] = None) { val lengthOfC = if (c.isDefined) 1 else 0 lengthOfC + 2 + lengthOfZ } - } object AxisOrder { diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/DataLayer.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/DataLayer.scala index 3a1e839bdd2..e09d50fc3e6 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/DataLayer.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/DataLayer.scala @@ -12,6 +12,7 @@ import com.scalableminds.webknossos.datastore.dataformats.layers.{ PrecomputedDataLayer, PrecomputedSegmentationLayer, WKWDataLayer, + WKWResolution, WKWSegmentationLayer, Zarr3DataLayer, Zarr3SegmentationLayer, @@ -227,6 +228,51 @@ trait DataLayerLike { // n-dimensional datasets = 3-dimensional datasets with additional coordinate axes def additionalAxes: Option[Seq[AdditionalAxis]] + // Datasets that are not in the WKW format use mags + def magsOpt: Option[List[MagLocator]] = this match { + case layer: AbstractDataLayer => layer.mags + case layer: AbstractSegmentationLayer => layer.mags + case layer: DataLayerWithMagLocators => Some(layer.getMags) + case _ => None + } + + def dataFormatOpt: Option[DataFormat.Value] = this match { + case layer: WKWDataLayer => Some(layer.dataFormat) + case layer: WKWSegmentationLayer => Some(layer.dataFormat) + case layer: ZarrDataLayer => Some(layer.dataFormat) + case layer: ZarrSegmentationLayer => Some(layer.dataFormat) + case layer: N5DataLayer => Some(layer.dataFormat) + case layer: N5SegmentationLayer => Some(layer.dataFormat) + case layer: PrecomputedDataLayer => Some(layer.dataFormat) + case layer: PrecomputedSegmentationLayer => Some(layer.dataFormat) + case layer: Zarr3DataLayer => Some(layer.dataFormat) + case layer: Zarr3SegmentationLayer => Some(layer.dataFormat) + // Abstract layers + case _ => None + } + + def numChannelsOpt: Option[Int] = this match { + case layer: AbstractDataLayer => layer.numChannels + case layer: AbstractSegmentationLayer => layer.numChannels + case layer: ZarrDataLayer => layer.numChannels + case layer: ZarrSegmentationLayer => layer.numChannels + case layer: N5DataLayer => layer.numChannels + case layer: N5SegmentationLayer => layer.numChannels + case layer: PrecomputedDataLayer => layer.numChannels + case layer: PrecomputedSegmentationLayer => layer.numChannels + case layer: Zarr3DataLayer => layer.numChannels + case layer: Zarr3SegmentationLayer => layer.numChannels + case _ => None + } + + def wkwResolutionsOpt: Option[List[WKWResolution]] = this match { + case layer: AbstractDataLayer => layer.wkwResolutions + case layer: AbstractSegmentationLayer => layer.wkwResolutions + case layer: WKWDataLayer => Some(layer.wkwResolutions) + case layer: WKWSegmentationLayer => Some(layer.wkwResolutions) + case _ => None + } + } object DataLayerLike { @@ -404,6 +450,19 @@ trait DataLayerWithMagLocators extends DataLayer { case _ => throw new Exception("Encountered unsupported layer format") } + def getMags: List[MagLocator] = + this match { + case layer: N5DataLayer => layer.mags + case layer: N5SegmentationLayer => layer.mags + case layer: PrecomputedDataLayer => layer.mags + case layer: PrecomputedSegmentationLayer => layer.mags + case layer: ZarrDataLayer => layer.mags + case layer: ZarrSegmentationLayer => layer.mags + case layer: Zarr3DataLayer => layer.mags + case layer: Zarr3SegmentationLayer => layer.mags + case _ => throw new Exception("Encountered unsupported layer format") + } + } trait SegmentationLayer extends DataLayer with SegmentationLayerLike { @@ -420,7 +479,11 @@ case class AbstractDataLayer( defaultViewConfiguration: Option[LayerViewConfiguration] = None, adminViewConfiguration: Option[LayerViewConfiguration] = None, coordinateTransformations: Option[List[CoordinateTransformation]] = None, - additionalAxes: Option[Seq[AdditionalAxis]] = None + additionalAxes: Option[Seq[AdditionalAxis]] = None, + mags: Option[List[MagLocator]] = None, + numChannels: Option[Int] = None, + dataFormat: Option[DataFormat.Value] = None, + wkwResolutions: Option[List[WKWResolution]] = None, ) extends DataLayerLike object AbstractDataLayer { @@ -435,7 +498,11 @@ object AbstractDataLayer { layer.defaultViewConfiguration, layer.adminViewConfiguration, layer.coordinateTransformations, - layer.additionalAxes + layer.additionalAxes, + layer.magsOpt, + layer.numChannelsOpt, + layer.dataFormatOpt, + layer.wkwResolutionsOpt ) implicit val jsonFormat: OFormat[AbstractDataLayer] = Json.format[AbstractDataLayer] @@ -452,7 +519,11 @@ case class AbstractSegmentationLayer( defaultViewConfiguration: Option[LayerViewConfiguration] = None, adminViewConfiguration: Option[LayerViewConfiguration] = None, coordinateTransformations: Option[List[CoordinateTransformation]] = None, - additionalAxes: Option[Seq[AdditionalAxis]] = None + additionalAxes: Option[Seq[AdditionalAxis]] = None, + mags: Option[List[MagLocator]] = None, + numChannels: Option[Int] = None, + dataFormat: Option[DataFormat.Value] = None, + wkwResolutions: Option[List[WKWResolution]] = None, ) extends SegmentationLayerLike object AbstractSegmentationLayer { @@ -469,7 +540,11 @@ object AbstractSegmentationLayer { layer.defaultViewConfiguration, layer.adminViewConfiguration, layer.coordinateTransformations, - layer.additionalAxes + layer.additionalAxes, + layer.magsOpt, + layer.numChannelsOpt, + layer.dataFormatOpt, + layer.wkwResolutionsOpt ) implicit val jsonFormat: OFormat[AbstractSegmentationLayer] = Json.format[AbstractSegmentationLayer]