55from cmlibs .utils .zinc .field import get_group_list
66from cmlibs .utils .zinc .group import group_get_highest_dimension , groups_have_same_local_contents
77from cmlibs .zinc .field import Field
8+ import logging
9+
10+
11+ logger = logging .getLogger (__name__ )
812
913
1014class AnnotationCategory (Enum ):
@@ -24,9 +28,18 @@ def get_group_name(self):
2428 """
2529 return '.' + self .name
2630
31+ def get_lower_name (self ):
32+ """
33+ :return: Lower case category name.
34+ """
35+ return self .name .lower ()
36+
2737 def is_connectable (self ):
2838 return self in (self .INDEPENDENT_NETWORK , self .NETWORK_GROUP_1 , self .NETWORK_GROUP_2 )
2939
40+ def is_connectable_different_annotation (self ):
41+ return self .is_connectable () and not (self == self .INDEPENDENT_NETWORK )
42+
3043
3144class Annotation :
3245 """
@@ -56,8 +69,8 @@ def decode_settings(self, settings_in: dict):
5669 assert (settings_in .get ("name" ) == self ._name ) and (settings_in .get ("term" ) == self ._term )
5770 settings_dimension = settings_in .get ("dimension" )
5871 if settings_dimension != self ._dimension :
59- print ( "WARNING: Segmentation Stitcher. Annotation with name" , self ._name , "term" , self ._term ,
60- "was dimension " , settings_dimension , "in settings, is now " , self ._dimension ,
72+ logger . warning ( " Segmentation Stitcher. Annotation with name " + self ._name , " term " + str ( self ._term ) +
73+ " was dimension " + str ( settings_dimension ) , "in settings, is now " + str ( self ._dimension ) +
6174 ". Have input files changed?" )
6275 settings_in ["dimension" ] = self ._dimension
6376 # update current settings to gain new ones and override old ones
@@ -125,6 +138,31 @@ def set_term(self, term):
125138 assert self ._term is None
126139 self ._term = term
127140
141+ def clear_term (self ):
142+ """
143+ Clear term to None, call in cases of mismatched terms for the same group name.
144+ """
145+ self ._term = None
146+
147+ def is_connectable (self ):
148+ """
149+ :return: True if annotation's category is connectable.
150+ """
151+ return self ._category .is_connectable ()
152+
153+ def is_connectable_with (self , other_annotation ):
154+ """
155+ Query whether ends annotated with self and other_annotation can be connected.
156+ :param other_annotation: Another Annotation object.
157+ :return: True if self and other_annotation are allowed to be connected by a link.
158+ """
159+ if self ._category .is_connectable () and (self ._category == other_annotation .get_category ()):
160+ if other_annotation is self :
161+ return True
162+ elif self ._category .is_connectable_different_annotation ():
163+ return True
164+ return False
165+
128166
129167def region_get_annotations (region , network_group1_keywords , network_group2_keywords , term_keywords ):
130168 """
@@ -146,6 +184,17 @@ def region_get_annotations(region, network_group1_keywords, network_group2_keywo
146184 annotations = []
147185 term_annotations = []
148186 datapoints = fieldmodule .findNodesetByFieldDomainType (Field .DOMAIN_TYPE_DATAPOINTS )
187+ # these terms have had slight mismatches with contents of url group, so explicitly matching:
188+ segment_name = region .getParent ().getName ()
189+ known_terms = {
190+ "epineurium" : "http://uri.interlex.org/base/ilx_0103892" ,
191+ "left cervical vagus nerve" : "http://uri.interlex.org/base/ilx_0794142" ,
192+ "right cervical vagus nerve" : "http://uri.interlex.org/base/ilx_0794141" ,
193+ "left thoracic vagus nerve" : "http://uri.interlex.org/base/ilx_0787543" ,
194+ "right thoracic vagus nerve" : "http://uri.interlex.org/base/ilx_0786664" ,
195+ "left vagus x nerve trunk" : "http://uri.interlex.org/base/ilx_0736691" ,
196+ "right vagus x nerve trunk" : "http://uri.interlex.org/base/ilx_0730515"
197+ }
149198 for group in groups :
150199 # clean up name to remove case and leading/trailing whitespace
151200 name = group .getName ().strip ()
@@ -159,7 +208,9 @@ def region_get_annotations(region, network_group1_keywords, network_group2_keywo
159208 continue # empty group
160209 if lower_name .isdigit ():
161210 continue # ignore as these can never be valid annotation names
162- category = AnnotationCategory .GENERAL
211+ if '<property name=' in lower_name :
212+ continue # don't want these groups output by mbfxml2ex
213+ category = AnnotationCategory .EXCLUDE if ('_' in lower_name ) else AnnotationCategory .GENERAL
163214 for keyword in network_group1_keywords :
164215 if keyword in lower_name :
165216 category = AnnotationCategory .NETWORK_GROUP_1
@@ -169,9 +220,10 @@ def region_get_annotations(region, network_group1_keywords, network_group2_keywo
169220 if keyword in lower_name :
170221 category = AnnotationCategory .NETWORK_GROUP_2
171222 break
172- annotation = Annotation (name , None , dimension , category )
223+ term = known_terms .get (lower_name )
224+ annotation = Annotation (name , term , dimension , category )
173225 is_term = False
174- if category == AnnotationCategory .GENERAL :
226+ if category in ( AnnotationCategory .GENERAL , AnnotationCategory . EXCLUDE ) :
175227 for keyword in term_keywords :
176228 if keyword in lower_name :
177229 is_term = True
@@ -180,23 +232,55 @@ def region_get_annotations(region, network_group1_keywords, network_group2_keywo
180232 term_annotations .append (annotation )
181233 else :
182234 annotations .append (annotation )
235+
236+ # sort annotations to have networks first, then general, lastly EXCLUDE to associate terms with earlier ones first
237+ ordered_categories = (
238+ AnnotationCategory .NETWORK_GROUP_1 ,
239+ AnnotationCategory .NETWORK_GROUP_2 ,
240+ AnnotationCategory .INDEPENDENT_NETWORK ,
241+ AnnotationCategory .GENERAL ,
242+ AnnotationCategory .EXCLUDE )
243+ assert len (ordered_categories ) == len (AnnotationCategory ) # in case new categories added, expand the above
244+ sorted_annotations = []
245+ for category in ordered_categories :
246+ for annotation in annotations :
247+ if annotation .get_category () == category :
248+ sorted_annotations .append (annotation )
249+
183250 for term_annotation in term_annotations :
184251 term = term_annotation .get_name ()
185252 term_group = fieldmodule .findFieldByName (term ).castGroup ()
186253 dimension = term_annotation .get_dimension ()
187- for annotation in annotations :
188- if annotation .get_term () is not None :
189- continue
254+ term_matched = False
255+ for annotation in sorted_annotations :
190256 if annotation .get_dimension () != dimension :
191257 continue
258+ if annotation .get_category () == AnnotationCategory .EXCLUDE :
259+ continue
192260 name = annotation .get_name ()
193261 name_group = fieldmodule .findFieldByName (name ).castGroup ()
194262 if groups_have_same_local_contents (name_group , term_group ):
195- annotation .set_term (term )
196- break
197- else :
198- print ("WARNING: Segmentation Stitcher. Did not find matching annotation name for term" , term ,
199- ". Adding separate annotation." )
263+ old_term = annotation .get_term ()
264+ if old_term :
265+ if old_term != term :
266+ logger .warning ("Segment " + segment_name + ": " +
267+ "Annotation name " + name + " already has term " + old_term +
268+ " but matched group with term " + term + ". Keeping original term." )
269+ else :
270+ # logger.info("Segment " + segment_name + ": " + "Annotation name " + name +
271+ # " discovered term " + term + ".")
272+ annotation .set_term (term )
273+ term_matched = True
274+ # do not break to allow all groups with matching contents to get the term
275+ else :
276+ known_term = known_terms .get (name .lower ())
277+ if known_term == term :
278+ logger .warning ("Segment " + segment_name + ": " +
279+ "Known annotation name " + name + " and term " + term + " groups differ. Using name group." )
280+ term_matched = True
281+ if not term_matched :
282+ logger .warning ("Segment " + segment_name + ": " +
283+ ". Did not find matching annotation name for term " + term + ". Adding separate annotation." )
200284 term_annotation .set_term (term )
201285 index = 0
202286 for annotation in annotations :
0 commit comments