diff --git a/Modules/SemanticRelations/include/mitkControlPointManager.h b/Modules/SemanticRelations/include/mitkControlPointManager.h index 89d3895344..cf8f3f570d 100644 --- a/Modules/SemanticRelations/include/mitkControlPointManager.h +++ b/Modules/SemanticRelations/include/mitkControlPointManager.h @@ -1,104 +1,118 @@ /*=================================================================== 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 MITKCONTROLPOINTMANAGER_H #define MITKCONTROLPOINTMANAGER_H #include // semantic relations module #include "mitkSemanticTypes.h" // mitk core #include namespace mitk { /** * @brief Provides helper functions that are needed to work with control points. * * These functions help to generate new control points, check for overlapping / containing control points or provide functionality * to find a fitting control point or even extend an already existing control point. */ /** * @brief Generates a control point from a given data node. * The date is extracted from the data node by using the 'DICOMHelper::GetDICOMDateFromDataNode'-function. * * @param datanode A data node pointer, whose date should be included in the newly generated control point. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GenerateControlPoint(const mitk::DataNode* datanode); /** * @brief Find and return a whole control point including its date given a specific control point UID. * * @param controlPointUID The control point UID as string. * @param allControlPoints All currently known control points of a specific case. * * @return The control point with its UID and the date. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GetControlPointByUID(const SemanticTypes::ID& controlPointUID, const SemanticTypes::ControlPointVector& allControlPoints); /** * @brief Returns an already existing control point from the given vector of control points. This existing control point has the * the same date (year, month, day) as the given single control point. * If no existing control point can be found an empty control point is returned. * * @param controlPoint The control point to check for existence. * @param allControlPoints The vector of already existing control points. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindExistingControlPoint(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ControlPointVector& allControlPoints); /** * @brief Returns an already existing close control point from the given vector of control points. This closest control point has a date * date that is within a certain distance-in-days to the given control point. * If no closest control point can be found within the distance threshold an empty control point is returned. * * @param controlPoint The control point to check for distance. * @param allControlPoints The vector of already existing control points. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindClosestControlPoint(const SemanticTypes::ControlPoint& controlPoint, SemanticTypes::ControlPointVector& allControlPoints); /** * @brief Returns the examination period to which the given control point belongs. * Each examination point holds a vector of control point UIDs so that the UID of the given control point can be compared against the UIDs of the vector. * An empty examination period is returned if, * - the given vector of examination periods is empty * - the examination periods do not contain any control point UIDs * - the UID of the given control point is not contained in any examination period * * @param controlPoint The control point of which the examination period should be found. * @param allExaminationPeriods All currently known examination periods of a specific case. + * + * @return The examination period that contains the given control point. + */ + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindContainingExaminationPeriod(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriodVector& allExaminationPeriods); + /** + * @brief Return the examination period to which the given data node belongs. + * The control point is used to find an already existing or the closest control point in the semantic relations storage. + * If such a control point is found, the 'FindClosestControlPoint'-function with this control point as an argument is used + * to actually find the corresponding examination period. + * + * @param caseID The current case identifier is defined by the given string. + * @param controlPoint The control point of which the examination period should be found. + * + * @return The examination period that fits the given data node. */ - MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindExaminationPeriod(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriodVector& allExaminationPeriods); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindFittingExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /** * @brief Return the examination period to which the given data node belongs. * The DICOM date of the data node is used to find an already existing or the closest control point in the semantic relations storage. - * If such a control point is found, the 'FindExaminationPeriod'-function with this control point as an argument is used + * If such a control point is found, the 'FindFittingExaminationPeriod'-function with this control point as an argument is used * to actually find the corresponding examination period. * * @param datanode A data node pointer, whose date should be included in the newly generated control point. * * @return The examination period that contains the given data node. */ - MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindExaminationPeriod(const DataNode* dataNode); + MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindFittingExaminationPeriod(const DataNode* dataNode); /** * @brief Sort the given vector of examination periods. * Each examination period has a vector of control point UIDs (stored in chronological order). * The examination periods can be sorted by comparing the first control points of the examination periods. * * @param allExaminationPeriods All currently known examination periods of a specific case. * @param allControlPoints All currently known control points of a specific case. */ MITKSEMANTICRELATIONS_EXPORT void SortExaminationPeriods(SemanticTypes::ExaminationPeriodVector& allExaminationPeriods, const SemanticTypes::ControlPointVector& allControlPoints); } // namespace mitk #endif // MITKCONTROLPOINTMANAGER_H diff --git a/Modules/SemanticRelations/src/mitkControlPointManager.cpp b/Modules/SemanticRelations/src/mitkControlPointManager.cpp index 5f56a67eef..7b0aee79eb 100644 --- a/Modules/SemanticRelations/src/mitkControlPointManager.cpp +++ b/Modules/SemanticRelations/src/mitkControlPointManager.cpp @@ -1,219 +1,222 @@ /*=================================================================== 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. ===================================================================*/ // semantic relations module #include "mitkControlPointManager.h" #include "mitkDICOMHelper.h" #include "mitkRelationStorage.h" #include "mitkSemanticRelationException.h" #include "mitkUIDGeneratorBoost.h" // mitk core #include mitk::SemanticTypes::ControlPoint mitk::GenerateControlPoint(const DataNode* datanode) { SemanticTypes::ControlPoint controlPoint; try { controlPoint = GetDICOMDateFromDataNode(datanode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot generate a control point from the DICOM tag of the given data node"; } controlPoint.UID = UIDGeneratorBoost::GenerateUID(); return controlPoint; } mitk::SemanticTypes::ControlPoint mitk::GetControlPointByUID(const SemanticTypes::ID& controlPointUID, const std::vector& allControlPoints) { auto lambda = [&controlPointUID](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPointUID; }; const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), lambda); mitk::SemanticTypes::ControlPoint controlPoint; if (existingControlPoint != allControlPoints.end()) { controlPoint = *existingControlPoint; } return controlPoint; } mitk::SemanticTypes::ControlPoint mitk::FindExistingControlPoint(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ControlPointVector& allControlPoints) { for (const auto& currentControlPoint : allControlPoints) { if (controlPoint.date == currentControlPoint.date) { return currentControlPoint; } } return SemanticTypes::ControlPoint(); } mitk::SemanticTypes::ControlPoint mitk::FindClosestControlPoint(const SemanticTypes::ControlPoint& controlPoint, SemanticTypes::ControlPointVector& allControlPoints) { if (allControlPoints.empty()) { return SemanticTypes::ControlPoint(); } // sort the vector of control points for easier lookup std::sort(allControlPoints.begin(), allControlPoints.end()); // new control point does not match an existing control point // check if the control point is close to an already existing control point std::vector::const_iterator it; for (it = allControlPoints.begin(); it != allControlPoints.end(); ++it) { if (controlPoint.date < it->date) { break; } } SemanticTypes::ControlPoint nextControlPoint; SemanticTypes::ControlPoint previousControlPoint; if (it == allControlPoints.begin()) { // new date is smaller ("older") than the smallest already existing control point nextControlPoint = *it; } else if (it != allControlPoints.end()) { // new date is greater ("newer") than an already existing control point, // but smaller ("older") than another already existing control point nextControlPoint = *it; previousControlPoint = *(--it); } else { // new date is greater ("newer") than the greatest already existing control point previousControlPoint = *(--it); } // test distance to next and previous time period double distanceToNextExaminationPeriod = nextControlPoint.DistanceInDays(controlPoint); double distanceToPreviousExaminationPeriod = previousControlPoint.DistanceInDays(controlPoint); SemanticTypes::ControlPoint closestControlPoint; int closestDistanceInDays = 0; if (distanceToNextExaminationPeriod < distanceToPreviousExaminationPeriod) { // control point is closer to the next control point closestControlPoint = nextControlPoint; closestDistanceInDays = distanceToNextExaminationPeriod; } else { // control point is closer to the previous control point closestControlPoint = previousControlPoint; closestDistanceInDays = distanceToPreviousExaminationPeriod; } int THRESHOLD_DISTANCE_IN_DAYS = 30; if (closestDistanceInDays <= THRESHOLD_DISTANCE_IN_DAYS) { return closestControlPoint; } return SemanticTypes::ControlPoint(); } mitk::SemanticTypes::ExaminationPeriod mitk::FindExaminationPeriod(const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriodVector& allExaminationPeriods) { for (const auto& examinationPeriod : allExaminationPeriods) { for (const auto& UID : examinationPeriod.controlPointUIDs) { if (controlPoint.UID == UID) { return examinationPeriod; } } } return SemanticTypes::ExaminationPeriod(); } -mitk::SemanticTypes::ExaminationPeriod mitk::FindExaminationPeriod(const DataNode* dataNode) +mitk::SemanticTypes::ExaminationPeriod mitk::FindFittingExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { - SemanticTypes::CaseID caseID = ""; - SemanticTypes::InformationType informationType; - SemanticTypes::ControlPoint controlPoint; - try - { - caseID = GetCaseIDFromDataNode(dataNode); - informationType = GetDICOMModalityFromDataNode(dataNode); - controlPoint = GetDICOMDateFromDataNode(dataNode); - } - catch (SemanticRelationException& e) - { - mitkReThrow(e) << "Cannot find an examination period. "; - } - SemanticTypes::ExaminationPeriod specificExaminationPeriod; SemanticTypes::ControlPoint specificControlPoint; // find the closest control point auto allControlPoints = RelationStorage::GetAllControlPointsOfCase(caseID); auto existingControlPoint = FindExistingControlPoint(controlPoint, allControlPoints); if (!existingControlPoint.UID.empty()) { specificControlPoint = existingControlPoint; } else { auto closestControlPoint = FindClosestControlPoint(controlPoint, allControlPoints); if (!closestControlPoint.UID.empty()) { specificControlPoint = closestControlPoint; } } // find the containing examination period auto allExaminationPeriods = RelationStorage::GetAllExaminationPeriodsOfCase(caseID); - return FindExaminationPeriod(specificControlPoint, allExaminationPeriods); + return FindContainingExaminationPeriod(specificControlPoint, allExaminationPeriods); +} + +mitk::SemanticTypes::ExaminationPeriod mitk::FindFittingExaminationPeriod(const DataNode* dataNode) +{ + SemanticTypes::CaseID caseID = ""; + SemanticTypes::ControlPoint controlPoint; + try + { + caseID = GetCaseIDFromDataNode(dataNode); + controlPoint = GetDICOMDateFromDataNode(dataNode); + } + catch (SemanticRelationException& e) + { + mitkReThrow(e) << "Cannot find an examination period. "; + } + + return FindFittingExaminationPeriod(caseID, controlPoint); } void mitk::SortExaminationPeriods(SemanticTypes::ExaminationPeriodVector& allExaminationPeriods, const SemanticTypes::ControlPointVector& allControlPoints) { auto lambda = [allControlPoints](const SemanticTypes::ExaminationPeriod& leftExaminationPeriod, const SemanticTypes::ExaminationPeriod& rightExaminationPeriod) { if (leftExaminationPeriod.controlPointUIDs.empty()) { return true; } if (rightExaminationPeriod.controlPointUIDs.empty()) { return false; } const auto leftUID = leftExaminationPeriod.controlPointUIDs.front(); const auto rightUID = rightExaminationPeriod.controlPointUIDs.front(); const auto& leftControlPoint = GetControlPointByUID(leftUID, allControlPoints); const auto& rightControlPoint = GetControlPointByUID(rightUID, allControlPoints); return leftControlPoint.date < rightControlPoint.date; }; std::sort(allExaminationPeriods.begin(), allExaminationPeriods.end(), lambda); } diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp index 47540a006f..d5477878d3 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsIntegration.cpp @@ -1,613 +1,613 @@ /*=================================================================== 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 image data node."; } SemanticTypes::CaseID caseID; SemanticTypes::ID imageID; SemanticTypes::InformationType informationType; SemanticTypes::ControlPoint controlPoint; try // retrieve information { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); informationType = GetDICOMModalityFromDataNode(imageNode); controlPoint = GenerateControlPoint(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given image data node."; } try // add and set information { RelationStorage::AddCase(caseID); RelationStorage::AddImage(caseID, imageID); AddInformationTypeToImage(imageNode, informationType); SetControlPointOfImage(imageNode, controlPoint); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given image data node."; } } void mitk::SemanticRelationsIntegration::RemoveImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID; SemanticTypes::ID imageID; try // retrieve information { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot remove the given image data node."; } try { RemoveInformationTypeFromImage(imageNode); UnlinkImageFromControlPoint(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot remove the given image data node."; } RelationStorage::RemoveImage(caseID, imageID); 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."; } 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; try { caseID = GetCaseIDFromDataNode(segmentationNode); AddLesion(caseID, lesion); LinkSegmentationToLesion(segmentationNode, lesion); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add given lesion and link the given segmentation data node."; } 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."; } SemanticTypes::CaseID caseID; SemanticTypes::ID segmentationNodeID; SemanticTypes::ID parentNodeID; try { caseID = GetCaseIDFromDataNode(segmentationNode); segmentationNodeID = GetIDFromDataNode(segmentationNode); parentNodeID = GetIDFromDataNode(parentNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given segmentation data node."; } 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; SemanticTypes::ID segmentationID; try { caseID = GetCaseIDFromDataNode(segmentationNode); segmentationID = GetIDFromDataNode(segmentationNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot link the given segmentation data node to the given lesion."; } if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { 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; SemanticTypes::ID segmentationID; try { caseID = GetCaseIDFromDataNode(segmentationNode); segmentationID = GetIDFromDataNode(segmentationNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot unlink the given segmentation data node from its lesion."; } RelationStorage::UnlinkSegmentationFromLesion(caseID, segmentationID); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::RemoveSegmentation(const DataNode* segmentationNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID; SemanticTypes::ID segmentationNodeID; try { caseID = GetCaseIDFromDataNode(segmentationNode); segmentationNodeID = GetIDFromDataNode(segmentationNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot remove the given segmentation data node."; } try { UnlinkSegmentationFromLesion(segmentationNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot remove the given segmentation data node."; } RelationStorage::RemoveSegmentation(caseID, segmentationNodeID); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::SetControlPointOfImage(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID; try { caseID = GetCaseIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot set the given control point for the given image data node."; } 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); try { if (!existingControlPoint.UID.empty()) { // found an already existing control point LinkImageToControlPoint(imageNode, existingControlPoint, false); } else { 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); + SemanticTypes::ExaminationPeriod examinationPeriod = FindContainingExaminationPeriod(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 (SemanticRelationException& e) { mitkReThrow(e) << "Cannot set the given control point for the given image data node."; } ClearControlPoints(caseID); NotifyObserver(caseID); } void mitk::SemanticRelationsIntegration::AddControlPointAndLinkImage(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID; try { caseID = GetCaseIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given control point and link the given image data node."; } if (SemanticRelationsInference::InstanceExists(caseID, controlPoint)) { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add already exists for the given case. \n Use 'LinkImageToControlPoint' instead."; } RelationStorage::AddControlPoint(caseID, controlPoint); try { LinkImageToControlPoint(imageNode, controlPoint, checkConsistence); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given control point and link the given image data node."; } } void mitk::SemanticRelationsIntegration::LinkImageToControlPoint(const DataNode* imageNode, const SemanticTypes::ControlPoint& controlPoint, bool /*checkConsistence*/) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID; SemanticTypes::ID imageID; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot link the image data node to the given control point."; } if (SemanticRelationsInference::InstanceExists(caseID, controlPoint)) { RelationStorage::LinkImageToControlPoint(caseID, imageID, controlPoint); } else { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to link does not exist for the given case."; } } void mitk::SemanticRelationsIntegration::UnlinkImageFromControlPoint(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot unlink the given image data node from its control point."; } SemanticTypes::ControlPoint controlPoint = RelationStorage::GetControlPointOfImage(caseID, imageID); RelationStorage::UnlinkImageFromControlPoint(caseID, imageID); 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::RenameExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) { if (SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) { RelationStorage::RenameExaminationPeriod(caseID, examinationPeriod); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " to overwrite does not exist for the given case."; } } 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) { SemanticTypes::CaseID caseID; try { caseID = GetCaseIDFromDataNode(imageNode); RemoveInformationTypeFromImage(imageNode); AddInformationTypeToImage(imageNode, informationType); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot set the given information type for the given image data node."; } 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 = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot add the given information type to the given image data node."; } 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 = ""; SemanticTypes::ID imageID = ""; try { caseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot remove the information type from the given image data node."; } 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::RemoveInformationType(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::RemoveControlPoint(caseID, controlPoint); } } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp index 163299ed1b..cd6d3297e7 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp @@ -1,275 +1,275 @@ /*=================================================================== 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. ===================================================================*/ // semantic relations plugin #include "QmitkDataNodeAddToSemanticRelationsAction.h" #include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" // semantic relations module #include #include #include #include #include #include #include #include // mitk gui common plugin #include // mitk core #include // qt #include // namespace that contains the concrete action namespace AddToSemanticRelationsAction { void Run(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, mitk::DataStorage* dataStorage, const mitk::DataNode* dataNode) { if (nullptr == dataNode) { return; } if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { AddImage(semanticRelationsIntegration, dataStorage, dataNode); } else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { AddSegmentation(semanticRelationsIntegration, dataStorage, dataNode); } } void AddImage(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, mitk::DataStorage* dataStorage, const mitk::DataNode* image) { if (nullptr == image) { return; } mitk::SemanticTypes::InformationType informationType; mitk::SemanticTypes::ExaminationPeriod examinationPeriod; mitk::SemanticRelationsDataStorageAccess::DataNodeVector allSpecificImages; try { mitk::SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(image); informationType = GetDICOMModalityFromDataNode(image); // see if the examination period - information type cell is already taken - examinationPeriod = FindExaminationPeriod(image); + examinationPeriod = FindFittingExaminationPeriod(image); auto semanticRelationsDataStorageAccess = mitk::SemanticRelationsDataStorageAccess(dataStorage); try { allSpecificImages = semanticRelationsDataStorageAccess.GetAllSpecificImages(caseID, informationType, examinationPeriod); } catch (const mitk::SemanticRelationException&) { // just continue since an exception means that there is no specific image } } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not add the selected image.", "The program wasn't able to correctly add the selected images.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); msgBox.exec(); } if (!allSpecificImages.empty()) { // examination period - information type cell is already taken // ask if cell should be overwritten QMessageBox::StandardButton answerButton = QMessageBox::question(nullptr, "Specific image already exists.", QString::fromStdString("Force overwriting existing image " + informationType + " at " + examinationPeriod.name + "?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { try { // remove already existent images at specific cell for (const auto& specificImage : allSpecificImages) { RemoveFromSemanticRelationsAction::Run(semanticRelationsIntegration, dataStorage, specificImage); } } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not add the selected image.", "The program wasn't able to correctly add the selected images.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); msgBox.exec(); } } else { // else case is: no overwriting return; } } - // specific image does not exists or has been removed; adding the image should work + // specific image does not exist or has been removed; adding the image should work try { semanticRelationsIntegration->AddImage(image); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not add the selected image.", "The program wasn't able to correctly add the selected images.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); msgBox.exec(); } } void AddSegmentation(mitk::SemanticRelationsIntegration* semanticRelationsIntegration, mitk::DataStorage* dataStorage, const mitk::DataNode* segmentation) { if (nullptr == segmentation) { return; } mitk::BaseData* baseData = segmentation->GetData(); if (nullptr == baseData) { return; } // continue with valid segmentation data // get parent node of the current segmentation node with the node predicate mitk::DataStorage::SetOfObjects::ConstPointer parentNodes = dataStorage->GetSources(segmentation, mitk::NodePredicates::GetImagePredicate(), false); if (parentNodes->empty()) { // segmentation without corresponding image will not be added QMessageBox msgBox(QMessageBox::Warning, "Could not add the selected segmentation.", "The program wasn't able to correctly add the selected segmentation.\n" "Reason: No parent image found"); msgBox.exec(); return; } // check for already existing, identifying base properties auto caseIDPropertyName = mitk::GetCaseIDDICOMProperty(); auto nodeIDPropertyName = mitk::GetNodeIDDICOMProperty(); mitk::BaseProperty* caseIDProperty = baseData->GetProperty(caseIDPropertyName.c_str()); mitk::BaseProperty* nodeIDProperty = baseData->GetProperty(nodeIDPropertyName.c_str()); if (nullptr == caseIDProperty || nullptr == nodeIDProperty) { MITK_INFO << "No DICOM tags for case and node identification found. Transferring DICOM tags from the parent node to the selected segmentation node."; mitk::SemanticTypes::CaseID caseID; mitk::SemanticTypes::ID nodeID; try { caseID = mitk::GetCaseIDFromDataNode(parentNodes->front()); nodeID = mitk::GetIDFromDataNode(parentNodes->front()); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not add the selected segmentation.", "The program wasn't able to correctly add the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.exec(); return; } // transfer DICOM tags to the segmentation node baseData->SetProperty(caseIDPropertyName, mitk::TemporoSpatialStringProperty::New(caseID)); // add UID to distinguish between different segmentations of the same parent node baseData->SetProperty(nodeIDPropertyName, mitk::TemporoSpatialStringProperty::New(nodeID + mitk::UIDGeneratorBoost::GenerateUID())); } // add the parent node if not already existent if (!mitk::SemanticRelationsInference::InstanceExists(parentNodes->front())) { AddImage(semanticRelationsIntegration, dataStorage, parentNodes->front()); } try { // add the segmentation with its parent image to the semantic relations storage semanticRelationsIntegration->AddSegmentation(segmentation, parentNodes->front()); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not add the selected segmentation.", "The program wasn't able to correctly add the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.exec(); return; } } } QmitkDataNodeAddToSemanticRelationsAction::QmitkDataNodeAddToSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) , QmitkAbstractSemanticRelationsAction(workbenchPartSite) { setText(tr("Add to semantic relations")); InitializeAction(); } QmitkDataNodeAddToSemanticRelationsAction::QmitkDataNodeAddToSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) , QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Add to semantic relations")); InitializeAction(); } QmitkDataNodeAddToSemanticRelationsAction::~QmitkDataNodeAddToSemanticRelationsAction() { // nothing here } void QmitkDataNodeAddToSemanticRelationsAction::InitializeAction() { connect(this, &QAction::triggered, this, &QmitkDataNodeAddToSemanticRelationsAction::OnActionTriggered); } void QmitkDataNodeAddToSemanticRelationsAction::OnActionTriggered(bool /*checked*/) { if (nullptr == m_SemanticRelationsIntegration) { return; } if (m_DataStorage.IsExpired()) { return; } auto dataNode = GetSelectedNode(); AddToSemanticRelationsAction::Run(m_SemanticRelationsIntegration.get(), m_DataStorage.Lock(), dataNode); } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp index 825a5abf7f..37c192b029 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp @@ -1,102 +1,180 @@ /*=================================================================== 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. ===================================================================*/ // semantic relations plugin #include "QmitkDataNodeSetControlPointAction.h" +#include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" // semantic relations module +#include #include +#include #include #include #include // semantic relations UI module #include "QmitkControlPointDialog.h" -// mitk gui common plugin -#include - // qt #include +#include QmitkDataNodeSetControlPointAction::QmitkDataNodeSetControlPointAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) , QmitkAbstractSemanticRelationsAction(workbenchPartSite) { setText(tr("Set control point")); m_Parent = parent; InitializeAction(); } QmitkDataNodeSetControlPointAction::QmitkDataNodeSetControlPointAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) , QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Set control point")); m_Parent = parent; InitializeAction(); } QmitkDataNodeSetControlPointAction::~QmitkDataNodeSetControlPointAction() { // nothing here } void QmitkDataNodeSetControlPointAction::InitializeAction() { connect(this, &QAction::triggered, this, &QmitkDataNodeSetControlPointAction::OnActionTriggered); } void QmitkDataNodeSetControlPointAction::OnActionTriggered(bool /*checked*/) { if (nullptr == m_SemanticRelationsIntegration) { return; } + if (m_DataStorage.IsExpired()) + { + return; + } + auto dataNode = GetSelectedNode(); if (dataNode.IsNull()) { return; } QmitkControlPointDialog* inputDialog = new QmitkControlPointDialog(m_Parent); inputDialog->setWindowTitle("Set control point"); inputDialog->SetCurrentDate(mitk::SemanticRelationsInference::GetControlPointOfImage(dataNode)); int dialogReturnValue = inputDialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } const QDate& userSelectedDate = inputDialog->GetCurrentDate(); mitk::SemanticTypes::ControlPoint controlPoint; controlPoint.UID = mitk::UIDGeneratorBoost::GenerateUID(); controlPoint.date = boost::gregorian::date(userSelectedDate.year(), userSelectedDate.month(), userSelectedDate.day()); + mitk::SemanticTypes::InformationType informationType; + mitk::SemanticTypes::ExaminationPeriod examinationPeriod; + mitk::SemanticRelationsDataStorageAccess::DataNodeVector allSpecificImages; + try + { + mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(dataNode); + informationType = mitk::SemanticRelationsInference::GetInformationTypeOfImage(dataNode); + // see if the examination period - information type cell is already taken + examinationPeriod = mitk::FindFittingExaminationPeriod(caseID, controlPoint); + auto semanticRelationsDataStorageAccess = mitk::SemanticRelationsDataStorageAccess(m_DataStorage.Lock()); + try + { + allSpecificImages = semanticRelationsDataStorageAccess.GetAllSpecificImages(caseID, informationType, examinationPeriod); + } + catch (const mitk::SemanticRelationException&) + { + // just continue since an exception means that there is no specific image + } + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not set the control point.", + "The program wasn't able to correctly set the control point.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); + } + + if (!allSpecificImages.empty()) + { + // examination period - information type cell is already taken + // ask if cell should be overwritten + QMessageBox::StandardButton answerButton = + QMessageBox::question(nullptr, + "Specific image already exists.", + QString::fromStdString("Force overwriting existing image " + informationType + " at " + examinationPeriod.name + "?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (answerButton == QMessageBox::Yes) + { + try + { + // remove already existent images at specific cell + for (const auto& specificImage : allSpecificImages) + { + RemoveFromSemanticRelationsAction::Run(m_SemanticRelationsIntegration.get(), m_DataStorage.Lock(), specificImage); + } + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not set the control point.", + "The program wasn't able to correctly set the control point.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); + } + } + else + { + // else case is: no overwriting + return; + } + } + + // specific image does not exist or has been removed; setting the control point should work try { m_SemanticRelationsIntegration->UnlinkImageFromControlPoint(dataNode); m_SemanticRelationsIntegration->SetControlPointOfImage(dataNode, controlPoint); } - catch (const mitk::SemanticRelationException&) + catch (const mitk::SemanticRelationException& e) { - return; + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not set the control point.", + "The program wasn't able to correctly set the control point.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); } } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.cpp index 64ea29b0e6..4075650f6f 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.cpp @@ -1,91 +1,171 @@ /*=================================================================== 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. ===================================================================*/ // semantic relations plugin #include "QmitkDataNodeSetInformationTypeAction.h" +#include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" // semantic relations module +#include +#include +#include #include #include -// mitk gui common plugin -#include - // qt #include +#include QmitkDataNodeSetInformationTypeAction::QmitkDataNodeSetInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) , QmitkAbstractSemanticRelationsAction(workbenchPartSite) { setText(tr("Set information type")); m_Parent = parent; InitializeAction(); } QmitkDataNodeSetInformationTypeAction::QmitkDataNodeSetInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) , QmitkAbstractSemanticRelationsAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Set information type")); m_Parent = parent; InitializeAction(); } QmitkDataNodeSetInformationTypeAction::~QmitkDataNodeSetInformationTypeAction() { // nothing here } void QmitkDataNodeSetInformationTypeAction::InitializeAction() { connect(this, &QAction::triggered, this, &QmitkDataNodeSetInformationTypeAction::OnActionTriggered); } void QmitkDataNodeSetInformationTypeAction::OnActionTriggered(bool /*checked*/) { if (nullptr == m_SemanticRelationsIntegration) { return; } + if (m_DataStorage.IsExpired()) + { + return; + } + auto dataNode = GetSelectedNode(); if (dataNode.IsNull()) { return; } QInputDialog* inputDialog = new QInputDialog(m_Parent); inputDialog->setWindowTitle(tr("Set information type of selected node")); inputDialog->setLabelText(tr("Information type:")); inputDialog->setTextValue(QString::fromStdString(mitk::SemanticRelationsInference::GetInformationTypeOfImage(dataNode))); inputDialog->setMinimumSize(250, 100); int dialogReturnValue = inputDialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } + mitk::SemanticTypes::InformationType informationType = inputDialog->textValue().toStdString(); + + mitk::SemanticTypes::ExaminationPeriod examinationPeriod; + mitk::SemanticTypes::ControlPoint controlPoint; + mitk::SemanticRelationsDataStorageAccess::DataNodeVector allSpecificImages; try { - m_SemanticRelationsIntegration->SetInformationType(dataNode, inputDialog->textValue().toStdString()); + mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(dataNode); + controlPoint = mitk::SemanticRelationsInference::GetControlPointOfImage(dataNode); + // see if the examination period - information type cell is already taken + examinationPeriod = mitk::FindFittingExaminationPeriod(caseID, controlPoint); + auto semanticRelationsDataStorageAccess = mitk::SemanticRelationsDataStorageAccess(m_DataStorage.Lock()); + try + { + allSpecificImages = semanticRelationsDataStorageAccess.GetAllSpecificImages(caseID, informationType, examinationPeriod); + } + catch (const mitk::SemanticRelationException&) + { + // just continue since an exception means that there is no specific image + } } - catch (const mitk::SemanticRelationException&) + catch (const mitk::SemanticRelationException& e) { - return; + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not set the information type.", + "The program wasn't able to correctly set the information type.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); + } + + if (!allSpecificImages.empty()) + { + // examination period - information type cell is already taken + // ask if cell should be overwritten + QMessageBox::StandardButton answerButton = + QMessageBox::question(nullptr, + "Specific image already exists.", + QString::fromStdString("Force overwriting existing image " + informationType + " at " + examinationPeriod.name + "?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (answerButton == QMessageBox::Yes) + { + try + { + // remove already existent images at specific cell + for (const auto& specificImage : allSpecificImages) + { + RemoveFromSemanticRelationsAction::Run(m_SemanticRelationsIntegration.get(), m_DataStorage.Lock(), specificImage); + } + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not set the information type.", + "The program wasn't able to correctly set the information type.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); + } + } + else + { + // else case is: no overwriting + return; + } + } + + try + { + m_SemanticRelationsIntegration->SetInformationType(dataNode, informationType); + } + catch (const mitk::SemanticRelationException& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox(QMessageBox::Warning, + "Could not set the information type.", + "The program wasn't able to correctly set the information type.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); + msgBox.exec(); } }