@@ -424,9 +424,9 @@ private Element[] trackCollectionToXml()
424424 public void processVertexLate ( final Spot vertex , final DepthFirstSearch < Spot , Link > search )
425425 {
426426 /*
427- * 1 root = 1 track, unless a track has several roots. Add
428- * the iterated vertex to the list of root to skip if
429- * needed.
427+ * 1 root = 1 track, unless a track has several
428+ * roots. Add the iterated vertex to the list of
429+ * root to skip if needed.
430430 */
431431 if ( vertex .incomingEdges ().isEmpty () )
432432 toSkip .add ( vertex );
@@ -520,6 +520,15 @@ private Element spotCollectionToXml()
520520 return spotCollectionElement ;
521521 }
522522
523+ /**
524+ * Collection of link feature names that we want to export in the mamut
525+ * file, but computed from the link data currently set in Mastodon. We used
526+ * this collection to avoid exporting a feature with identical name in the
527+ * case a feature imported as the same name.
528+ */
529+ private final static Set < String > IMPORTED_LINK_BUILTIN_FEATURES = new HashSet <>( Arrays .asList (
530+ EDGE_SOURCE_ATTRIBUTE , EDGE_TARGET_ATTRIBUTE ) );
531+
523532 private Element edgeToXml ( final Link edge , final int sourceSpotID , final int targetSpotID )
524533 {
525534 final Collection < Attribute > attributes = new ArrayList <>();
@@ -529,9 +538,38 @@ private Element edgeToXml( final Link edge, final int sourceSpotID, final int ta
529538 attributes .add ( new Attribute ( EDGE_TARGET_ATTRIBUTE , Integer .toString ( targetSpotID ) ) );
530539
531540 // Link features.
532- linkFeatureProjections .forEach ( p -> attributes .add ( new Attribute (
533- p .attributeName ,
534- Double .toString ( p .projection .value ( edge ) ) ) ) );
541+ for ( final ExportFeatureProjection < Link > p : linkFeatureProjections )
542+ {
543+ final String attName ;
544+ final String origName = p .attributeName ;
545+ /*
546+ * If the model to export was imported from a TrackMate or a MaMuT
547+ * file, it will already contain features with the same name that
548+ * the builtin feature we added just above. Which will cause an
549+ * error.
550+ *
551+ * To avoid this, rename these imported features.
552+ */
553+ if ( IMPORTED_LINK_BUILTIN_FEATURES .contains ( origName ) )
554+ {
555+ final String importedAttName = "IMPORTED_" + origName ;
556+ attName = importedAttName ;
557+ }
558+ else if ( origName .startsWith ( "IMPORTED_" ) )
559+ {
560+ // We skip features that have been re-imported.
561+ continue ;
562+ }
563+ else
564+ {
565+ // All good.
566+ attName = origName ;
567+ }
568+
569+ attributes .add ( new Attribute (
570+ attName ,
571+ Double .toString ( p .projection .value ( edge ) ) ) );
572+ }
535573
536574 final Element edgeElement = new Element ( EDGE_TAG );
537575 edgeElement .setAttributes ( attributes );
@@ -556,9 +594,20 @@ private Element trackToXml( final Spot root )
556594 return trackElement ;
557595 }
558596
597+ /**
598+ * Collection of spot feature names that we want to export in the mamut
599+ * file, but computed from the actual position, etc, currently set in
600+ * Mastodon. We used this collection to avoid exporting a feature with
601+ * identical name in the case a feature imported as the same name.
602+ */
603+ private final static Set < String > IMPORTED_SPOT_BUILTIN_FEATURES = new HashSet <>( Arrays .asList ( ID_FEATURE_NAME , LABEL_FEATURE_NAME , POSITION_X_FEATURE_NAME ,
604+ POSITION_Y_FEATURE_NAME , POSITION_Z_FEATURE_NAME , FRAME_FEATURE_NAME ,
605+ POSITION_T_FEATURE_NAME , QUALITY_FEATURE_NAME , VISIBILITY_FEATURE_NAME ,
606+ RADIUS_FEATURE_NAME ) );
607+
559608 private Element spotToXml ( final Spot spot )
560609 {
561- final Collection < Attribute > attributes = new ArrayList <>();
610+ final List < Attribute > attributes = new ArrayList <>();
562611
563612 // Id.
564613 attributes .add ( new Attribute ( ID_FEATURE_NAME , Integer .toString ( spot .getInternalPoolIndex () ) ) );
@@ -583,10 +632,38 @@ private Element spotToXml( final Spot spot )
583632 final double meanRadius = Arrays .stream ( eig .getRealEigenvalues () ).map ( Math ::sqrt ).average ().getAsDouble ();
584633 attributes .add ( new Attribute ( RADIUS_FEATURE_NAME , Double .toString ( meanRadius ) ) );
585634
586- // Spot features.
587- spotFeatureProjections .forEach ( p -> attributes .add ( new Attribute (
588- p .attributeName ,
589- Double .toString ( p .projection .value ( spot ) ) ) ) );
635+ for (final ExportFeatureProjection < Spot > p : spotFeatureProjections )
636+ {
637+ final String attName ;
638+ final String origName = p .attributeName ;
639+ /*
640+ * If the model to export was imported from a TrackMate or a MaMuT
641+ * file, it will already contain features with the same name that
642+ * the builtin feature we added just above. Which will cause an
643+ * error.
644+ *
645+ * To avoid this, rename these imported features.
646+ */
647+ if ( IMPORTED_SPOT_BUILTIN_FEATURES .contains ( origName ) )
648+ {
649+ final String importedAttName = "IMPORTED_" + origName ;
650+ attName = importedAttName ;
651+ }
652+ else if ( origName .startsWith ( "IMPORTED_" ) )
653+ {
654+ // We skip features that have been re-imported.
655+ continue ;
656+ }
657+ else
658+ {
659+ // All good.
660+ attName = origName ;
661+ }
662+
663+ attributes .add ( new Attribute (
664+ attName ,
665+ Double .toString ( p .projection .value ( spot ) ) ) );
666+ }
590667
591668 final Element spotElement = new Element ( SPOT_ELEMENT_TAG );
592669 spotElement .setAttributes ( attributes );
@@ -652,8 +729,8 @@ private static Document getSAXParsedDocument( final String fileName )
652729 }
653730
654731 /**
655- * Tries to recover the TrackMate dimension from the unit string and the spatial and
656- * time units.
732+ * Tries to recover the TrackMate dimension from the unit string and the
733+ * spatial and time units.
657734 *
658735 * @param units
659736 * the unit string.
0 commit comments