@@ -13,9 +13,11 @@ import {StatusMessage} from 'neuroglancer/status';
13
13
import { TrackableBoolean , TrackableBooleanCheckbox } from 'neuroglancer/trackable_boolean' ;
14
14
import { getPositionSummary , SelectedAnnotationState , UserLayerWithAnnotations } from 'neuroglancer/ui/annotations' ;
15
15
import { HidingList } from 'neuroglancer/ui/hiding_list' ;
16
+ import { br } from 'neuroglancer/util/br' ;
16
17
import { Borrowed , Owned } from 'neuroglancer/util/disposable' ;
17
18
import { mat4 , transformVectorByMat4 , vec3 } from 'neuroglancer/util/geom' ;
18
19
import { formatIntegerBounds , formatIntegerPoint } from 'neuroglancer/util/spatial_units' ;
20
+ import { Uint64 } from 'neuroglancer/util/uint64' ;
19
21
import { ColorWidget } from 'neuroglancer/widget/color' ;
20
22
import { MinimizableGroupWidget } from 'neuroglancer/widget/minimizable_group' ;
21
23
import { RangeWidget } from 'neuroglancer/widget/range' ;
@@ -314,6 +316,8 @@ export class AnnotationLayerView extends Tab {
314
316
const importCSVButton = document . createElement ( 'button' ) ;
315
317
const importCSVForm = document . createElement ( 'form' ) ;
316
318
const importCSVFileSelect = document . createElement ( 'input' ) ;
319
+ const segmentCSVOverrideCheckbox = document . createElement ( 'input' ) ;
320
+ const segmentCSVOverrideLabel = document . createElement ( 'label' ) ;
317
321
exportToCSVButton . id = 'exportToCSVButton' ;
318
322
exportToCSVButton . textContent = 'Export to CSV' ;
319
323
exportToCSVButton . addEventListener ( 'click' , ( ) => {
@@ -329,12 +333,19 @@ export class AnnotationLayerView extends Tab {
329
333
} ) ;
330
334
importCSVForm . appendChild ( importCSVFileSelect ) ;
331
335
importCSVFileSelect . addEventListener ( 'change' , ( ) => {
332
- this . importCSV ( importCSVFileSelect . files ) ;
336
+ this . importCSV ( importCSVFileSelect . files , segmentCSVOverrideCheckbox . checked ) ;
333
337
importCSVForm . reset ( ) ;
334
338
} ) ;
335
339
importCSVFileSelect . classList . add ( 'neuroglancer-hidden-button' ) ;
340
+ segmentCSVOverrideLabel . textContent = 'Select segments on import (Experimental): ' ;
341
+ segmentCSVOverrideLabel . title =
342
+ 'This requires that the segmentation source of the annotation layer when importing the CSV file is the same as the segmentation source when the file was exported. Imported IDs may be outdated.'
343
+ segmentCSVOverrideCheckbox . type = 'checkbox' ;
344
+
336
345
const csvContainer = document . createElement ( 'span' ) ;
337
- csvContainer . append ( exportToCSVButton , importCSVButton , importCSVForm ) ;
346
+ csvContainer . append (
347
+ exportToCSVButton , importCSVButton , br ( ) , segmentCSVOverrideLabel ,
348
+ segmentCSVOverrideCheckbox , importCSVForm ) ;
338
349
this . groupAnnotations . appendFixedChild ( csvContainer ) ;
339
350
}
340
351
@@ -1047,34 +1058,38 @@ export class AnnotationLayerView extends Tab {
1047
1058
}
1048
1059
1049
1060
// TODO: pull request to papa repo
1050
- private betterPapa = ( inputFile : File | Blob ) : Promise < any > => {
1051
- return new Promise ( ( resolve ) => {
1052
- Papa . parse ( inputFile , {
1053
- complete : ( results : any ) => {
1054
- resolve ( results ) ;
1055
- }
1061
+ private betterPapa = ( inputFile : File | Blob ) :
1062
+ Promise < any > => {
1063
+ return new Promise ( ( resolve ) => {
1064
+ Papa . parse ( inputFile , {
1065
+ complete : ( results : any ) => {
1066
+ resolve ( results ) ;
1067
+ }
1068
+ } ) ;
1056
1069
} ) ;
1057
- } ) ;
1058
- }
1070
+ }
1059
1071
1060
- private stringToVec3 = ( input : string ) : vec3 => {
1061
- // format: (x, y, z)
1062
- let raw = input . split ( '' ) ;
1063
- raw . shift ( ) ;
1064
- raw . pop ( ) ;
1065
- let list = raw . join ( '' ) ;
1066
- let val = list . split ( ',' ) . map ( v => parseInt ( v , 10 ) ) ;
1067
- return vec3 . fromValues ( val [ 0 ] , val [ 1 ] , val [ 2 ] ) ;
1068
- }
1072
+ private stringToVec3 = ( input : string ) :
1073
+ vec3 => {
1074
+ // format: (x, y, z)
1075
+ let list = input . replace ( / [ { ( ) } [ \] ] / g, '' ) ;
1076
+ let val = list . split ( ',' ) . map ( v => parseInt ( v , 10 ) ) ;
1077
+ return vec3 . fromValues ( val [ 0 ] , val [ 1 ] , val [ 2 ] ) ;
1078
+ }
1069
1079
1070
- private dimensionsToVec3 = ( input : string ) : vec3 => {
1071
- // format: A × B × C
1072
- let raw = input . replace ( / s / g, '' ) ;
1073
- let val = raw . split ( '×' ) . map ( v => parseInt ( v , 10 ) ) ;
1074
- return vec3 . fromValues ( val [ 0 ] , val [ 1 ] , val [ 2 ] ) ;
1075
- }
1080
+ private dimensionsToVec3 = ( input : string ) : vec3 => {
1081
+ // format: A × B × C
1082
+ let raw = input . replace ( / s / g, '' ) ;
1083
+ let val = raw . split ( '×' ) . map ( v => parseInt ( v , 10 ) ) ;
1084
+ return vec3 . fromValues ( val [ 0 ] , val [ 1 ] , val [ 2 ] ) ;
1085
+ }
1086
+ private textToPoint = ( point : string , transform : mat4 , dimension ?: boolean ) => {
1087
+ const parsedVec = dimension ? this . dimensionsToVec3 ( point ) : this . stringToVec3 ( point ) ;
1088
+ const spatialPoint = this . voxelSize . spatialFromVoxel ( tempVec3 , parsedVec ) ;
1089
+ return vec3 . transformMat4 ( vec3 . create ( ) , spatialPoint , transform ) ;
1090
+ }
1076
1091
1077
- private async importCSV ( files : FileList | null ) {
1092
+ private async importCSV ( files : FileList | null , segmentOverride : boolean = false ) {
1078
1093
const rawAnnotations = < Annotation [ ] > [ ] ;
1079
1094
let successfulImport = 0 ;
1080
1095
@@ -1088,61 +1103,59 @@ export class AnnotationLayerView extends Tab {
1088
1103
if ( ! rawData . data . length ) {
1089
1104
continue ;
1090
1105
}
1091
- const annStrings = rawData . data ;
1106
+ const annStrings = < string [ ] [ ] > rawData . data ;
1092
1107
const csvIdToRealAnnotationIdMap : { [ key : string ] : string } = { } ;
1093
1108
const childStorage : { [ key : string ] : string [ ] } = { } ;
1094
- const textToPoint = ( point : string , transform : mat4 , dimension ?: boolean ) => {
1095
- const parsedVec = dimension ? this . dimensionsToVec3 ( point ) : this . stringToVec3 ( point ) ;
1096
- const spatialPoint = this . voxelSize . spatialFromVoxel ( tempVec3 , parsedVec ) ;
1097
- return vec3 . transformMat4 ( vec3 . create ( ) , spatialPoint , transform ) ;
1098
- } ;
1099
1109
let row = - 1 ;
1110
+
1100
1111
for ( const annProps of annStrings ) {
1101
1112
row ++ ;
1102
- const type = annProps [ 7 ] ;
1113
+ const type = annProps [ 7 ] . toLowerCase ( ) ;
1103
1114
const parentId = annProps [ 6 ] ;
1104
1115
const annotationID : string | undefined = annProps [ 8 ] ;
1105
1116
const tags = annProps [ 3 ] ;
1117
+ const segments = annProps [ 5 ] ;
1106
1118
let raw = < Annotation > { id : makeAnnotationId ( ) , description : annProps [ 4 ] } ;
1107
1119
1108
1120
switch ( type ) {
1109
- case 'AABB ' :
1110
- case 'Line ' :
1121
+ case 'aabb ' :
1122
+ case 'line ' :
1111
1123
raw . type =
1112
- type === 'Line ' ? AnnotationType . LINE : AnnotationType . AXIS_ALIGNED_BOUNDING_BOX ;
1113
- ( < Line > raw ) . pointA = textToPoint ( annProps [ 0 ] , this . annotationLayer . globalToObject ) ;
1114
- ( < Line > raw ) . pointB = textToPoint ( annProps [ 1 ] , this . annotationLayer . globalToObject ) ;
1124
+ type === 'line ' ? AnnotationType . LINE : AnnotationType . AXIS_ALIGNED_BOUNDING_BOX ;
1125
+ ( < Line > raw ) . pointA = this . textToPoint ( annProps [ 0 ] , this . annotationLayer . globalToObject ) ;
1126
+ ( < Line > raw ) . pointB = this . textToPoint ( annProps [ 1 ] , this . annotationLayer . globalToObject ) ;
1115
1127
break ;
1116
- case 'Point ' :
1128
+ case 'point ' :
1117
1129
raw . type = AnnotationType . POINT ;
1118
- ( < Point > raw ) . point = textToPoint ( annProps [ 0 ] , this . annotationLayer . globalToObject ) ;
1130
+ ( < Point > raw ) . point = this . textToPoint ( annProps [ 0 ] , this . annotationLayer . globalToObject ) ;
1119
1131
break ;
1120
- case 'Ellipsoid ' :
1132
+ case 'ellipsoid ' :
1121
1133
raw . type = AnnotationType . ELLIPSOID ;
1122
- ( < Ellipsoid > raw ) . center = textToPoint ( annProps [ 0 ] , this . annotationLayer . globalToObject ) ;
1134
+ ( < Ellipsoid > raw ) . center =
1135
+ this . textToPoint ( annProps [ 0 ] , this . annotationLayer . globalToObject ) ;
1123
1136
( < Ellipsoid > raw ) . radii =
1124
- textToPoint ( annProps [ 2 ] , this . annotationLayer . globalToObject , true ) ;
1137
+ this . textToPoint ( annProps [ 2 ] , this . annotationLayer . globalToObject , true ) ;
1125
1138
break ;
1126
- case 'Line Strip' :
1127
- case 'Line Strip*' :
1128
- case 'Spoke ' :
1129
- case 'Spoke *' :
1130
- case 'Collection ' :
1131
- if ( type === 'Line Strip' || type === 'Line Strip*' ) {
1139
+ case 'line Strip' :
1140
+ case 'line Strip*' :
1141
+ case 'spoke ' :
1142
+ case 'spoke *' :
1143
+ case 'collection ' :
1144
+ if ( type === 'line Strip' || type === 'line Strip*' ) {
1132
1145
raw . type = AnnotationType . LINE_STRIP ;
1133
1146
( < LineStrip > raw ) . connected = true ;
1134
- ( < LineStrip > raw ) . looped = type === 'Line Strip*' ;
1135
- } else if ( type === 'Spoke ' || type === 'Spoke *' ) {
1147
+ ( < LineStrip > raw ) . looped = type === 'line Strip*' ;
1148
+ } else if ( type === 'spoke ' || type === 'spoke *' ) {
1136
1149
raw . type = AnnotationType . SPOKE ;
1137
1150
( < Spoke > raw ) . connected = true ;
1138
- ( < Spoke > raw ) . wheeled = type === 'Spoke *' ;
1151
+ ( < Spoke > raw ) . wheeled = type === 'spoke *' ;
1139
1152
} else {
1140
1153
raw . type = AnnotationType . COLLECTION ;
1141
1154
( < Collection > raw ) . connected = false ;
1142
1155
}
1143
1156
( < Collection > raw ) . childrenVisible = new TrackableBoolean ( false , true ) ;
1144
1157
( < Collection > raw ) . source =
1145
- textToPoint ( annProps [ 0 ] , this . annotationLayer . globalToObject ) ;
1158
+ this . textToPoint ( annProps [ 0 ] , this . annotationLayer . globalToObject ) ;
1146
1159
( < Collection > raw ) . entry = ( index : number ) =>
1147
1160
( < LocalAnnotationSource > this . annotationLayer . source )
1148
1161
. get ( ( < Collection > raw ) . entries [ index ] ) ;
@@ -1192,7 +1205,11 @@ export class AnnotationLayerView extends Tab {
1192
1205
} ) ;
1193
1206
}
1194
1207
// Segments not supported
1195
-
1208
+ // getSelectedAssocatedSegment(this.annotationLayer)
1209
+ // naively add segment directly from excel
1210
+ if ( segments && segmentOverride ) {
1211
+ raw . segments = segments . split ( ',' ) . map ( ( s : string ) => Uint64 . parseString ( s ) ) ;
1212
+ }
1196
1213
rawAnnotations . push ( raw ) ;
1197
1214
}
1198
1215
successfulImport ++ ;
0 commit comments