Skip to content

Commit

Permalink
ENH: Add API for finding terminology from codes
Browse files Browse the repository at this point in the history
Often the segment category&type and anatomic region codes are available in multiple contexts.
This commit adds FindTerminologyNames and FindAnatomicContextNames methods to the terminology logic that allows getting a list of contexts where the codes are listed in.

The methods can also return the terminology objects, which makes it very easy to get properties (such as name and color) from codes.
  • Loading branch information
lassoan committed May 10, 2024
1 parent 3a008b9 commit 1f1dd73
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@
#include "vtkLoggingMacros.h"

// VTK includes
#include <vtkCollection.h>
#include <vtkDirectory.h>
#include <vtkNew.h>
#include <vtkSmartPointer.h>
#include <vtkObjectFactory.h>
#include <vtkSmartPointer.h>
#include <vtkStringArray.h>
#include <vtkVariant.h>
#include <vtksys/SystemTools.hxx>
#include <vtkDirectory.h>

// STD includes
#include <algorithm>
Expand Down Expand Up @@ -1656,8 +1657,7 @@ bool vtkSlicerTerminologiesModuleLogic::GetTypeInTerminologyCategory(
rapidjson::Value& typeObject = this->Internal->GetTypeInTerminologyCategory(terminologyName, categoryId, typeId);
if (typeObject.IsNull())
{
vtkErrorMacro("GetTypeInTerminologyCategory: Failed to find type '" << typeId.CodeMeaning << "' in category '"
<< categoryId.CodeMeaning << "' in terminology '" << terminologyName << "'");
// Not found. This is not an error, as the type may not exist in the terminology and this method can be used to check for existence.
return false;
}

Expand Down Expand Up @@ -2439,3 +2439,122 @@ bool vtkSlicerTerminologiesModuleLogic::FindTypeInTerminologyBy3dSlicerLabel(std

return false;
}

//-----------------------------------------------------------------------------
std::vector<std::string> vtkSlicerTerminologiesModuleLogic::FindTerminologyNames(
std::string categoryCodingSchemeDesignator, std::string categoryCodeValue,
std::string typeCodingSchemeDesignator, std::string typeCodeValue,
std::string typeModifierCodingSchemeDesignator, std::string typeModifierCodeValue,
std::vector<std::string> preferredTerminologyNames,
vtkCollection* foundEntries/*=nullptr*/)
{
std::vector<std::string> foundTerminologyNames;
if (categoryCodingSchemeDesignator.empty() || categoryCodeValue.empty())
{
vtkErrorMacro("FindTerminologyEntries: Category is not specified");
return foundTerminologyNames;
}
CodeIdentifier categoryId(categoryCodingSchemeDesignator, categoryCodeValue);

if (typeCodingSchemeDesignator.empty() || typeCodeValue.empty())
{
vtkErrorMacro("FindTerminologyEntries: Type is not specified");
return foundTerminologyNames;
}
CodeIdentifier typeId(typeCodingSchemeDesignator, typeCodeValue);

if (preferredTerminologyNames.empty())
{
// Terminology names are not specified, so search in all available terminologies
this->GetLoadedTerminologyNames(preferredTerminologyNames);
}

// Find terminology entries in each preferred terminology
for (std::string terminologyName : preferredTerminologyNames)
{
vtkNew<vtkSlicerTerminologyType> typeObject;
if (!this->GetTypeInTerminologyCategory(terminologyName, categoryId, typeId, typeObject))
{
continue;
}
if (typeModifierCodingSchemeDesignator.empty() && typeModifierCodeValue.empty())
{
// Type without modifier
foundTerminologyNames.push_back(terminologyName);
if (foundEntries)
{
foundEntries->AddItem(typeObject);
}
}
else
{
// Type with a modifier
vtkNew<vtkSlicerTerminologyType> modifiedTypeObject;
if (this->GetTypeModifierInTerminologyType(terminologyName, categoryId, typeId,
CodeIdentifier(typeModifierCodingSchemeDesignator, typeModifierCodeValue), modifiedTypeObject))
{
foundTerminologyNames.push_back(terminologyName);
foundEntries->AddItem(typeObject);
}
}
}

return foundTerminologyNames;
}

//-----------------------------------------------------------------------------
std::vector<std::string> vtkSlicerTerminologiesModuleLogic::FindAnatomicContextNames(
std::string anatomicRegionCodingSchemeDesignator, std::string anatomicRegionCodeValue,
std::string anatomicRegionModifierCodingSchemeDesignator, std::string anatomicRegionModifierCodeValue,
std::vector<std::string> preferredAnatomicContextNames,
vtkCollection* foundEntries/*=nullptr*/)
{
std::vector<std::string> foundAnatomicContextNames;
if (anatomicRegionCodingSchemeDesignator.empty() || anatomicRegionCodeValue.empty())
{
vtkErrorMacro("FindAnatomicContextNames: anatomicRegion is not specified");
return foundAnatomicContextNames;
}
CodeIdentifier anatomicRegionId(anatomicRegionCodingSchemeDesignator, anatomicRegionCodeValue);

if (preferredAnatomicContextNames.empty())
{
// Anatomic context names are not specified, so search in all available terminologies
this->GetLoadedAnatomicContextNames(preferredAnatomicContextNames);
}

// Find terminology entries in each preferred anatomic context
for (std::string anatomicContextName : preferredAnatomicContextNames)
{
vtkNew<vtkSlicerTerminologyType> regionObject;
if (!this->GetRegionInAnatomicContext(anatomicContextName, anatomicRegionId, regionObject))
{
continue;
}
if (anatomicRegionModifierCodingSchemeDesignator.empty() && anatomicRegionModifierCodeValue.empty())
{
// Anatomic region without modifier
foundAnatomicContextNames.push_back(anatomicContextName);
if (foundEntries)
{
foundEntries->AddItem(regionObject);
}
}
else
{
// Anatomic region with a modifier
vtkNew<vtkSlicerTerminologyType> modifiedRegionObject;
if (this->GetRegionModifierInAnatomicRegion(anatomicContextName, anatomicRegionId,
CodeIdentifier(anatomicRegionModifierCodingSchemeDesignator, anatomicRegionModifierCodeValue), modifiedRegionObject))
{
foundAnatomicContextNames.push_back(anatomicContextName);
if (foundEntries)
{
foundEntries->AddItem(regionObject);
}
}
}
}

return foundAnatomicContextNames;
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class VTK_SLICER_TERMINOLOGIES_LOGIC_EXPORT vtkSlicerTerminologiesModuleLogic :
public:
CodeIdentifier()
{ };
CodeIdentifier(std::string codingSchemeDesignator, std::string codeValue, std::string codeMeaning)
CodeIdentifier(std::string codingSchemeDesignator, std::string codeValue, std::string codeMeaning=std::string())
: CodingSchemeDesignator(codingSchemeDesignator)
, CodeValue(codeValue)
, CodeMeaning(codeMeaning)
Expand Down Expand Up @@ -106,6 +106,24 @@ class VTK_SLICER_TERMINOLOGIES_LOGIC_EXPORT vtkSlicerTerminologiesModuleLogic :
/// from the categories found in the given terminology
/// \return Success flag
bool FindCategoriesInTerminology(std::string terminologyName, std::vector<CodeIdentifier>& categories, std::string search);

/// Return collection of vtkSlicerTerminologyEntry objects designated by the given codes.
/// \param preferredTerminologyNames List of terminology names in order of preference. If an empty list is provided then all terminologies are searched.
std::vector<std::string> FindTerminologyNames(
std::string categoryCodingSchemeDesignator, std::string categoryCodeValue,
std::string typeCodingSchemeDesignator, std::string typeCodeValue,
std::string typeModifierCodingSchemeDesignator, std::string typeModifierCodeValue,
std::vector<std::string> preferredTerminologyNames,
vtkCollection* foundEntries=nullptr);

/// Return list of anatomic context names containing the specified anatomic region.
/// \param preferredAnatomicContextNames List of anatomic context names in order of preference. If an empty list is provided then all context are searched.
std::vector<std::string> FindAnatomicContextNames(
std::string anatomicRegionCodingSchemeDesignator, std::string anatomicRegionCodeValue,
std::string anatomicRegionModifierCodingSchemeDesignator, std::string anatomicRegionModifierCodeValue,
std::vector<std::string> preferredAnatomicContextNames,
vtkCollection* foundEntries=nullptr);

/// Get a category with given name from a terminology
/// \param category Output argument containing the details of the found category if any (if return value is true)
/// \return Success flag
Expand Down
20 changes: 10 additions & 10 deletions Modules/Loadable/Terminologies/Logic/vtkSlicerTerminologyType.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,16 @@ void vtkSlicerTerminologyType::PrintSelf(ostream& os, vtkIndent indent)
{
Superclass::PrintSelf(os,indent);

os << indent << "RecommendedDisplayRGBValue: ("
<< this->RecommendedDisplayRGBValue[0] << ","
<< this->RecommendedDisplayRGBValue[1] << ","
<< this->RecommendedDisplayRGBValue[2] << ")\n";
os << indent << "SlicerLabel: " << (this->SlicerLabel?this->SlicerLabel:"NULL") << "\n";
os << indent << "SNOMEDCTConceptID: " << (this->SNOMEDCTConceptID?this->SNOMEDCTConceptID:"NULL") << "\n";
os << indent << "UMLSConceptUID: " << (this->UMLSConceptUID?this->UMLSConceptUID:"NULL") << "\n";
os << indent << "Cid: " << (this->Cid?this->Cid:"NULL") << "\n";
os << indent << "ContextGroupName: " << (this->ContextGroupName?this->ContextGroupName:"NULL") << "\n";
os << indent << "HasModifiers: " << (this->HasModifiers?"true":"false") << "\n";
os << indent << "RecommendedDisplayRGBValue: ("
<< int(this->RecommendedDisplayRGBValue[0]) << ","
<< int(this->RecommendedDisplayRGBValue[1]) << ","
<< int(this->RecommendedDisplayRGBValue[2]) << ")\n";
os << indent << "SlicerLabel: " << (this->SlicerLabel?this->SlicerLabel:"NULL") << "\n";
os << indent << "SNOMEDCTConceptID: " << (this->SNOMEDCTConceptID?this->SNOMEDCTConceptID:"NULL") << "\n";
os << indent << "UMLSConceptUID: " << (this->UMLSConceptUID?this->UMLSConceptUID:"NULL") << "\n";
os << indent << "Cid: " << (this->Cid?this->Cid:"NULL") << "\n";
os << indent << "ContextGroupName: " << (this->ContextGroupName?this->ContextGroupName:"NULL") << "\n";
os << indent << "HasModifiers: " << (this->HasModifiers?"true":"false") << "\n";
}

//----------------------------------------------------------------------------
Expand Down

0 comments on commit 1f1dd73

Please sign in to comment.