diff --git a/Modules/SemanticRelations/include/mitkSemanticRelationsIntegration.h b/Modules/SemanticRelations/include/mitkSemanticRelationsIntegration.h index ae65f4309c..2babb30592 100644 --- a/Modules/SemanticRelations/include/mitkSemanticRelationsIntegration.h +++ b/Modules/SemanticRelations/include/mitkSemanticRelationsIntegration.h @@ -1,311 +1,314 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKSEMANTICRELATIONSINTEGRATION_H #define MITKSEMANTICRELATIONSINTEGRATION_H #include // semantic relations module #include "mitkISemanticRelationsObservable.h" #include "mitkISemanticRelationsObserver.h" #include "mitkSemanticTypes.h" // mitk core #include namespace mitk { /** * @brief The API provides functions to manipulate image relations and instances * that are helpful during follow-up examination, like control-points (time period), * types of the images or lesions that may be visible on multiple images. * * The class is able to generate IDs from given data nodes using DICOM information. * These IDs are used to identify the corresponding instances of a specific case. * The case can also be directly identified by the given case ID. * * In order for most functions to work the case ID has to be used as a parameter. * If not, these functions do nothing. * * The class implements the ISemanticRelationsObservable interface to allow observers to * be informed about changes in the semantic relation storage. */ class MITKSEMANTICRELATIONS_EXPORT SemanticRelationsIntegration : public ISemanticRelationsObservable { public: /************************************************************************/ /* functions to implement the observer pattern */ /************************************************************************/ /** * @brief Adds the given concrete observer to the vector that holds all currently registered observer. * If the observer is already registered, it will not be added to the observer vector. * * @param observer The concrete observer to register. */ virtual void AddObserver(ISemanticRelationsObserver* observer) override; /** * @brief Removes the given concrete observer from the vector that holds all currently registered observer. * * @param observer The concrete observer to unregister. */ virtual void RemoveObserver(ISemanticRelationsObserver* observer) override; /************************************************************************/ /* functions to add / remove instances / attributes */ /************************************************************************/ /** * @brief Add the given image to the set of already existing images. * The date is extracted from the DICOM data of the image node and is compared to already existing control points in the semantic relations model. * The function tries to find a fitting control point or to extend an already existing control point, if the extracted control point is close to * any other, already existing control point. * Finally, the image is linked to the correct control point. * * @pre The given image data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * * @param imageNode The current case identifier and node identifier is extracted from the given image data node, which contains DICOM information about the case and the node. */ void AddImage(const DataNode* imageNode); /** * @brief Remove the given image from the set of already existing images. * * @pre The given image data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * * @param imageNode The current case identifier and node identifier is extracted from the given image data node, which contains DICOM information about the case and the node. */ void RemoveImage(const DataNode* imageNode); /** * @brief Add a newly created lesion to the set of already existing lesions - with no connection to a specific image / segmentation of the case data. * * @pre The UID of the lesion must not already exist for a lesion instance. * @throw SemanticRelationException, it the UID of the lesion already exists for a lesion instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param lesion The lesion instance to add. */ void AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); /** * @brief Overwrite an already existing lesion instance (this may be useful to overwrite the lesion with a different lesion class). * * @pre The UID of the lesion has to exist for a lesion instance. * @throw SemanticRelationException, if the UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param lesion The lesion instance that overwrites an existing lesion. */ void OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); /** * @brief Add a newly created lesion to the set of already existing lesions. The lesion is added and a reference to - * the lesion is added to the segmentation data. If the segmentation is already linked to a lesion, the + * the lesion is added to the segmentation. If the segmentation is already linked to a lesion, the * old linkage is overwritten (this can be checked via 'IsRepresentingALesion'). * * @pre The given segmentation data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given segmentation data node is invalid (==nullptr). * @pre The UID of the lesion must not already exist for a lesion instance. * @throw SemanticRelationException, if the UID of the lesion already exists for a lesion instance (this can be checked via 'InstanceExists'). * - * @param segmentationNode The segmentation identifier is extracted from the given data node. The segmentation node has DICOM information from its parent node. + * @param segmentationNode The segmentation identifier is extracted from the given segmentation data node. The segmentation node has DICOM information from its parent node. * @param lesion The lesion instance to add and link. */ void AddLesionAndLinkSegmentation(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion); /** * @brief Remove the given lesion from the set of already existing lesions. * * @pre The UID of the lesion has to exist for a lesion instance. * @throw SemanticRelationException, if the UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). * @pre The function needs to assure that no segmentation is still representing (linked to) this lesion. * @throw SemanticRelationException, if the lesion instance to remove is still linked to by any segmentation (this can be checked via 'GetAllSegmentationsOfLesion'). * * @param caseID The current case identifier is defined by the given string. * @param lesion The lesion instance to remove. */ void RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); /** * @brief Add a segmentation instance to the set of already existing segmentations - with no connection to a specific lesion. * - * @param segmentationNode The segmentation identifier is extracted from the given data node. The segmentation node has DICOM information from its parent node. + * @param segmentationNode The segmentation identifier is extracted from the given segmentation data node. The segmentation node has DICOM information from its parent node. * @param parentNode The node identifier of the parent node is extracted from the given parent data node. */ void AddSegmentation(const DataNode* segmentationNode, const DataNode* parentNode); /** * @brief Link the given segmentation instance to an an already existing lesion instance. If the segmentation is already linked to a lesion instance, the * old linkage is overwritten (this can be checked via 'IsRepresentingALesion'). * * @pre The given segmentation data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given segmentation data node is invalid (==nullptr). * @pre The UID of the lesion has to exist for a lesion instance. * @throw SemanticRelationException, if the UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). * - * @param segmentationNode The segmentation identifier is extracted from the given data node. The segmentation node has DICOM information from its parent node. + * @param segmentationNode The segmentation identifier is extracted from the given segmentation data node. The segmentation node has DICOM information from its parent node. * @param lesion The lesion instance to link. */ void LinkSegmentationToLesion(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion); /** * @brief Unlink the given segmentation instance from the linked lesion instance. * The lesion may stay unlinked to any segmentation. * * @pre The given segmentation data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given segmentation data node is invalid (==nullptr). * - * @param segmentationNode The segmentation identifier is extracted from the given data node. The segmentation node has DICOM information from its parent node. + * @param segmentationNode The segmentation identifier is extracted from the given segmentation data node. The segmentation node has DICOM information from its parent node. */ void UnlinkSegmentationFromLesion(const DataNode* segmentationNode); /** * @brief Remove the given segmentation from the set of already existing segmentations. * * @pre The given segmentation data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given segmentation data node is invalid (==nullptr). * - * @param segmentationNode The segmentation identifier is extracted from the given data node. The segmentation node has DICOM information from its parent node. + * @param segmentationNode The segmentation identifier is extracted from the given segmentation data node. The segmentation node has DICOM information from its parent node. */ void RemoveSegmentation(const DataNode* segmentationNode); /** - * @brief Set the control point for the given data node. + * @brief Set the control point for the given image. * - * @pre The given data node has to be valid (!nullptr). - * @throw SemanticRelationException, if the given data node is invalid (==nullptr). + * @pre The given image data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). + * + * @param imageNode The current case identifier and node identifier is extracted from the given image data node, which contains DICOM information about the case and the node. + * @param controlPoint The control point instance which is used for the given image. */ - void SetControlPointOfData(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint); + void SetControlPointOfImage(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint); /** - * @brief Add a newly created control point to the set of already existing control points. A reference to the control point is added to the given data. + * @brief Add a newly created control point to the set of already existing control points. A reference to the control point is added to the given image. * This function combines adding a control point and linking it, since a control point with no associated data is not allowed. * - * @pre The given data node has to be valid (!nullptr). - * @throw SemanticRelationException, if the given data node is invalid (==nullptr). + * @pre The given image data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * @pre The UID of the control point must not already exist for a control point instance. * @throw SemanticRelationException, if the UID of the control point already exists for a control point instance (this can be checked via 'InstanceExists'). * @pre The given control point must not already be contained in an existing control point interval. * @throw SemanticRelationException, if the given control point is already contained in an existing control point interval (this can be checked via 'CheckContainingControlPoint'). - * @pre The given control point must contain the date of the given data node (if parameter 'checkConsistence = true'). - * @throw SemanticRelationException, if the given control point does not contain the date of the given data node and 'checkConsistence = true' (this can be checked via 'ControlPointManager::InsideControlPoint'). + * @pre The given control point must contain the date of the given image data node (if parameter 'checkConsistence = true'). + * @throw SemanticRelationException, if the given control point does not contain the date of the given image data node and 'checkConsistence = true' (this can be checked via 'ControlPointManager::InsideControlPoint'). * - * @param dataNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. + * @param imageNode The current case identifier and node identifier is extracted from the given image data node, which contains DICOM information about the case and the node. * @param controlPoint The control point instance to add. For a newly added control point always has "startDate = endDate". - * @param checkConsistence If true, the function checks, whether the date of the data node actually lies inside the control point to link. + * @param checkConsistence If true, the function checks, whether the date of the image data node actually lies inside the control point to link. */ - void AddControlPointAndLinkData(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence = true); + void AddControlPointAndLinkImage(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence = true); /** - * @brief Link the given data to an already existing control point. + * @brief Link the given image to an already existing control point. * - * @pre The given data node has to be valid (!nullptr). - * @throw SemanticRelationException, if the given data node is invalid (==nullptr). + * @pre The given image data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * @pre The UID of the control point has to exist for a control point instance. * @throw SemanticRelationException, if the UID of the control point does not exists for a control point instance (this can be checked via 'InstanceExists'). - * @pre The given control point must contain the date of the given data node (if parameter 'checkConsistence = true'). - * @throw SemanticRelationException, if the given control point does not contain the date of the given data node and 'checkConsistence = true' (this can be checked via 'ControlPointManager::InsideControlPoint'). + * @pre The given control point must contain the date of the given image data node (if parameter 'checkConsistence = true'). + * @throw SemanticRelationException, if the given control point does not contain the date of the given image data node and 'checkConsistence = true' (this can be checked via 'ControlPointManager::InsideControlPoint'). * - * @param dataNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. + * @param imageNode The current case identifier and node identifier is extracted from the given image data node, which contains DICOM information about the case and the node. * @param controlPoint The control point instance to link. - * @param checkConsistence If true, the function checks, whether the date of the data node actually lies inside the control point to link. + * @param checkConsistence If true, the function checks, whether the date of the image data node actually lies inside the control point to link. */ - void LinkDataToControlPoint(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence = true); + void LinkImageToControlPoint(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence = true); /** * @brief Unlink the given image from the linked control point. - * If data is unlinked from a control point, the function needs to check whether the control point is still linked to any other data: - * - if not, the control point instance will be removed (has to be removed since a control point with no associated data is not allowed). + * If an image is unlinked from a control point, the function needs to check whether the control point is still linked to any other image: + * - if not, the control point instance will be removed (has to be removed since a control point with no associated image is not allowed). * - if so, the function has to make sure that the control point instance is shortened to its minimum time period (e.g. moving the end point to an earlier date). * - * @param dataNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. + * @param imageNode The current case identifier and node identifier is extracted from the given image data node, which contains DICOM information about the case and the node. */ - void UnlinkImageFromControlPoint(const DataNode* dataNode); + void UnlinkImageFromControlPoint(const DataNode* imageNode); /** * @brief Add an examination period instance to the set of already existing examination periods - with no connection to a specific control point. * * @pre The UID of the examination period must not already exist for an examination period instance. * @throw SemanticRelationException, if the UID of the examination period already exists for a examination period instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param examinationPeriod The examination period to add. */ void AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); /** * @brief Add a control point to the vector of control point UIDs of an existing examination period. * * @pre The UID of the control point has to exist for a control point instance. * @throw SemanticRelationException, if the UID of the control point does not exists for a control point instance (this can be checked via 'InstanceExists'). * @pre The UID of the examination period must not already exist for an examination period instance. * @throw SemanticRelationException, if the UID of the examination period already exists for a examination period instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param controlPoint The control point instance to add to the examination period. * @param examinationPeriod The examination period to which the control point should be added. */ void AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod); /** * @brief Set (and possibly overwrite) the information type of the given image. * An already associated information type might be removed if is not referenced by any other image: * * @pre The given image data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * @post If the information type instance did not exist before, it is now added. * - * @param imageNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. + * @param imageNode The current case identifier is extracted from the given image data node, which contains DICOM information about the case. * @param informationType An information type that identifies the corresponding information type instance. */ void SetInformationType(const DataNode* imageNode, const SemanticTypes::InformationType& informationType); /** * @brief Set the information type of the given image. * * @pre The given image data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * @post If the information type instance did not exist before, it is now added. * - * @param imageNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. + * @param imageNode The current case identifier is extracted from the given image data node, which contains DICOM information about the case. * @param informationType An information type that identifies the corresponding information type instance. */ void AddInformationTypeToImage(const DataNode* imageNode, const SemanticTypes::InformationType& informationType); /** * @brief Remove the information type of the given image. * If the information type is removed, the function needs to check whether the information type is referenced by any other image: * - if not, the information type instance can be removed (has to be removed since an information type with no associated image is not allowed). * - if so, the information type is just removed from the given image. * * @pre The given image data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * - * @param imageNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. + * @param imageNode The current case identifier is extracted from the given image data node, which contains DICOM information about the case. */ void RemoveInformationTypeFromImage(const DataNode* imageNode); private: /** * @brief A vector that stores the currently registered observer of this observable subject. */ static std::vector m_ObserverVector; /** * @brief The class notifies (updates) the observer with a given case ID. * The view's caseID was set before in the GUI. The parts of the view that observe changes in the semantic relations are only updated, * if the given case ID is equal to the observer's current caseID and thus the observer currently shows the semantic information of the given case. * * @param caseID The caseID that identifies the currently active patient / case. */ virtual void NotifyObserver(const mitk::SemanticTypes::CaseID& caseID) const override; /** * @brief Remove all control points from the storage that are not referenced by any image anymore. * This might happen if an image has been removed (and unlinked from the corresponding control point) * or if the user sets a new control point for an image manually in the GUI. * * @param caseID The current case identifier is defined by the given string. */ void ClearControlPoints(const SemanticTypes::CaseID& caseID); }; } // namespace mitk #endif // MITKSEMANTICRELATIONSINTEGRATION_H diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp index 889772ec71..e2b6060bbd 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp @@ -1,446 +1,442 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSemanticRelationsIntegration.h" // semantic relations module #include "mitkControlPointManager.h" #include "mitkDICOMHelper.h" #include "mitkNodePredicates.h" #include "mitkRelationStorage.h" #include "mitkSemanticRelationException.h" #include "mitkSemanticRelationsInference.h" #include "mitkUIDGeneratorBoost.h" // multi label module #include // c++ #include #include std::vector mitk::SemanticRelationsIntegration::m_ObserverVector; void mitk::SemanticRelationsIntegration::AddObserver(ISemanticRelationsObserver* observer) { std::vector::iterator existingObserver = std::find(m_ObserverVector.begin(), m_ObserverVector.end(), observer); if (existingObserver != m_ObserverVector.end()) { // no need to add the already existing observer return; } m_ObserverVector.push_back(observer); } void mitk::SemanticRelationsIntegration::RemoveObserver(ISemanticRelationsObserver* observer) { m_ObserverVector.erase(std::remove(m_ObserverVector.begin(), m_ObserverVector.end(), observer), m_ObserverVector.end()); } /************************************************************************/ /* functions to add / remove instances / attributes */ /************************************************************************/ void mitk::SemanticRelationsIntegration::AddImage(const DataNode* imageNode) { if (nullptr == imageNode) { - mitkThrowException(SemanticRelationException) << "Not a valid data node."; + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } - // continue with a valid data node SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID nodeID = GetIDFromDataNode(imageNode); RelationStorage::AddCase(caseID); RelationStorage::AddImage(caseID, nodeID); SemanticTypes::InformationType informationType = GetDICOMModalityFromDataNode(imageNode); AddInformationTypeToImage(imageNode, informationType); // set the correct control point for this image SemanticTypes::ControlPoint controlPoint = GenerateControlPoint(imageNode); - SetControlPointOfData(imageNode, controlPoint); + SetControlPointOfImage(imageNode, controlPoint); } void mitk::SemanticRelationsIntegration::RemoveImage(const DataNode* imageNode) { if (nullptr == imageNode) { - mitkThrowException(SemanticRelationException) << "Not a valid data node."; + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } - // continue with a valid data node SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID nodeID = GetIDFromDataNode(imageNode); RemoveInformationTypeFromImage(imageNode); UnlinkImageFromControlPoint(imageNode); RelationStorage::RemoveImage(caseID, nodeID); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to add already exists for the given case."; } else { RelationStorage::AddLesion(caseID, lesion); NotifyObserver(caseID); } } void mitk::SemanticRelationsIntegration::OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { RelationStorage::OverwriteLesion(caseID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to overwrite does not exist for the given case."; } } void mitk::SemanticRelationsIntegration::AddLesionAndLinkSegmentation(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); AddLesion(caseID, lesion); LinkSegmentationToLesion(segmentationNode, lesion); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { SemanticTypes::IDVector allSegmentationIDsOfLesion = RelationStorage::GetAllSegmentationIDsOfLesion(caseID, lesion); if (allSegmentationIDsOfLesion.empty()) { // no more segmentations are linked to the specific lesion // the lesion can be removed from the storage RelationStorage::RemoveLesion(caseID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to remove is still referred to by a segmentation node. Lesion will not be removed."; } } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to remove does not exist for the given case."; } } void mitk::SemanticRelationsIntegration::AddSegmentation(const DataNode* segmentationNode, const DataNode* parentNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } if (nullptr == parentNode) { mitkThrowException(SemanticRelationException) << "Not a valid parent data node."; } - // continue with a valid data node SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); SemanticTypes::ID segmentationNodeID = GetIDFromDataNode(segmentationNode); SemanticTypes::ID parentNodeID = GetIDFromDataNode(parentNode); RelationStorage::AddSegmentation(caseID, segmentationNodeID, parentNodeID); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::LinkSegmentationToLesion(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); RelationStorage::LinkSegmentationToLesion(caseID, segmentationID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to link does not exist for the given case."; } } void mitk::SemanticRelationsIntegration::UnlinkSegmentationFromLesion(const DataNode* segmentationNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); RelationStorage::UnlinkSegmentationFromLesion(caseID, segmentationID); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::RemoveSegmentation(const DataNode* segmentationNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } - // continue with a valid data node SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); SemanticTypes::ID segmentationNodeID = GetIDFromDataNode(segmentationNode); RelationStorage::RemoveSegmentation(caseID, segmentationNodeID); NotifyObserver(caseID); } -void mitk::SemanticRelationsIntegration::SetControlPointOfData(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint) +void mitk::SemanticRelationsIntegration::SetControlPointOfImage(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint) { - if (nullptr == dataNode) + if (nullptr == imageNode) { - mitkThrowException(SemanticRelationException) << "Not a valid data node."; + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ControlPointVector allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); // need to check if an already existing control point fits/contains the user control point SemanticTypes::ControlPoint existingControlPoint = FindExistingControlPoint(controlPoint, allControlPoints); if (!existingControlPoint.UID.empty()) { try { // found an already existing control point - LinkDataToControlPoint(dataNode, existingControlPoint, false); + LinkImageToControlPoint(imageNode, existingControlPoint, false); } catch (const SemanticRelationException&) { - mitkThrowException(SemanticRelationException) << "The data can not be linked. Inconsistency in the semantic relations storage assumed."; + mitkThrowException(SemanticRelationException) << "The image data node can not be linked. Inconsistency in the semantic relations storage assumed."; } } else { try { - AddControlPointAndLinkData(dataNode, controlPoint, false); + AddControlPointAndLinkImage(imageNode, controlPoint, false); // added a new control point // find closest control point to add the new control point to the correct examination period SemanticTypes::ControlPoint closestControlPoint = FindClosestControlPoint(controlPoint, allControlPoints); SemanticTypes::ExaminationPeriodVector allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); SemanticTypes::ExaminationPeriod examinationPeriod = FindExaminationPeriod(closestControlPoint, allExaminationPeriods); if (examinationPeriod.UID.empty()) { // no closest control point (exceed threshold) or no examination period found // create a new examination period for this control point and add it to the storage examinationPeriod.UID = UIDGeneratorBoost::GenerateUID(); examinationPeriod.name = "New examination period " + std::to_string(allExaminationPeriods.size()); AddExaminationPeriod(caseID, examinationPeriod); } // add the control point to the (newly created or found / close) examination period AddControlPointToExaminationPeriod(caseID, controlPoint, examinationPeriod); } catch (const SemanticRelationException&) { - mitkThrowException(SemanticRelationException) << "The data can not be linked. Inconsistency in the semantic relations storage assumed."; + mitkThrowException(SemanticRelationException) << "The image data node can not be linked. Inconsistency in the semantic relations storage assumed."; } } ClearControlPoints(caseID); NotifyObserver(caseID); } -void mitk::SemanticRelationsIntegration::AddControlPointAndLinkData(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence) +void mitk::SemanticRelationsIntegration::AddControlPointAndLinkImage(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence) { - if (nullptr == dataNode) + if (nullptr == imageNode) { - mitkThrowException(SemanticRelationException) << "Not a valid data node."; + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); if (SemanticRelationsInference::InstanceExists(caseID, controlPoint)) { - mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add already exists for the given case. \n Use 'LinkDataToControlPoint' instead."; + mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add already exists for the given case. \n Use 'LinkImageToControlPoint' instead."; } RelationStorage::AddControlPoint(caseID, controlPoint); - LinkDataToControlPoint(dataNode, controlPoint, checkConsistence); + LinkImageToControlPoint(imageNode, controlPoint, checkConsistence); } -void mitk::SemanticRelationsIntegration::LinkDataToControlPoint(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence) +void mitk::SemanticRelationsIntegration::LinkImageToControlPoint(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence) { - if (nullptr == dataNode) + if (nullptr == imageNode) { - mitkThrowException(SemanticRelationException) << "Not a valid data node."; + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); if (SemanticRelationsInference::InstanceExists(caseID, controlPoint)) { - SemanticTypes::ID dataID = GetIDFromDataNode(dataNode); + SemanticTypes::ID dataID = GetIDFromDataNode(imageNode); RelationStorage::LinkImageToControlPoint(caseID, dataID, controlPoint); } else { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to link does not exist for the given case."; } } -void mitk::SemanticRelationsIntegration::UnlinkImageFromControlPoint(const DataNode* dataNode) +void mitk::SemanticRelationsIntegration::UnlinkImageFromControlPoint(const DataNode* imageNode) { - if (nullptr == dataNode) + if (nullptr == imageNode) { - mitkThrowException(SemanticRelationException) << "Not a valid data node."; + mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); - SemanticTypes::ID dataID = GetIDFromDataNode(dataNode); + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + SemanticTypes::ID dataID = GetIDFromDataNode(imageNode); SemanticTypes::ControlPoint controlPoint = RelationStorage::GetControlPointOfImage(caseID, dataID); RelationStorage::UnlinkImageFromControlPoint(caseID, dataID); ClearControlPoints(caseID); } void mitk::SemanticRelationsIntegration::AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) { if (SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) { mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " to add already exists for the given case."; } else { RelationStorage::AddExaminationPeriod(caseID, examinationPeriod); } } void mitk::SemanticRelationsIntegration::AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod) { if (!SemanticRelationsInference::InstanceExists(caseID, controlPoint)) { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add does not exist for the given case."; } if (!SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) { mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " does not exist for the given case. \n Use 'AddExaminationPeriod' before."; } RelationStorage::AddControlPointToExaminationPeriod(caseID, controlPoint, examinationPeriod); } void mitk::SemanticRelationsIntegration::SetInformationType(const DataNode* imageNode, const SemanticTypes::InformationType& informationType) { RemoveInformationTypeFromImage(imageNode); AddInformationTypeToImage(imageNode, informationType); SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::AddInformationTypeToImage(const DataNode* imageNode, const SemanticTypes::InformationType& informationType) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); RelationStorage::AddInformationTypeToImage(caseID, imageID, informationType); } void mitk::SemanticRelationsIntegration::RemoveInformationTypeFromImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); SemanticTypes::InformationType originalInformationType = RelationStorage::GetInformationTypeOfImage(caseID, imageID); RelationStorage::RemoveInformationTypeFromImage(caseID, imageID); // check for further references to the removed information type std::vector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); for (const auto& otherImageID : allImageIDsVectorValue) { SemanticTypes::InformationType otherInformationType = RelationStorage::GetInformationTypeOfImage(caseID, otherImageID); if (otherInformationType == originalInformationType) { // found the information type in another image -> cannot remove the information type from the case return; } } // given information type was not referred by any other image of the case -> the information type can be removed from the case RelationStorage::RemoveInformationTypeFromCase(caseID, originalInformationType); } /************************************************************************/ /* private functions */ /************************************************************************/ void mitk::SemanticRelationsIntegration::NotifyObserver(const SemanticTypes::CaseID& caseID) const { for (auto& observer : m_ObserverVector) { observer->Update(caseID); } } void mitk::SemanticRelationsIntegration::ClearControlPoints(const SemanticTypes::CaseID& caseID) { SemanticTypes::ControlPointVector allControlPointsOfCase = RelationStorage::GetAllControlPointsOfCase(caseID); std::vector allImageIDsVectorValue = RelationStorage::GetAllImageIDsOfCase(caseID); SemanticTypes::ControlPointVector referencedControlPoints; for (const auto& imageID : allImageIDsVectorValue) { auto controlPointOfImage = RelationStorage::GetControlPointOfImage(caseID, imageID); referencedControlPoints.push_back(controlPointOfImage); } std::sort(allControlPointsOfCase.begin(), allControlPointsOfCase.end()); std::sort(referencedControlPoints.begin(), referencedControlPoints.end()); SemanticTypes::ControlPointVector nonReferencedControlPoints; std::set_difference(allControlPointsOfCase.begin(), allControlPointsOfCase.end(), referencedControlPoints.begin(), referencedControlPoints.end(), std::inserter(nonReferencedControlPoints, nonReferencedControlPoints.begin())); auto allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); for (const auto& controlPoint : nonReferencedControlPoints) { const auto& examinationPeriod = FindExaminationPeriod(controlPoint, allExaminationPeriods); RelationStorage::RemoveControlPointFromExaminationPeriod(caseID, controlPoint, examinationPeriod); RelationStorage::RemoveControlPointFromCase(caseID, controlPoint); } }