/* * * Copyright (C) 2016-2021, J. Riesmeier, Oldenburg, Germany * All rights reserved. See COPYRIGHT file for details. * * Source file for class TID1411_VolumetricROIMeasurements * * Author: Joerg Riesmeier * */ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ #include "dcmtk/dcmsr/cmr/tid1411.h" #include "dcmtk/dcmsr/cmr/tid15def.h" #include "dcmtk/dcmsr/cmr/logger.h" #include "dcmtk/dcmsr/codes/dcm.h" #include "dcmtk/dcmsr/codes/ncit.h" #include "dcmtk/dcmsr/codes/sct.h" #include "dcmtk/dcmsr/codes/umls.h" #include "dcmtk/dcmsr/dsrtpltn.h" #include "dcmtk/dcmdata/dcdeftag.h" #include "dcmtk/dcmdata/dcuid.h" // helper macros for checking the return value of API calls #define CHECK_RESULT(call) if (result.good()) result = call #define STORE_RESULT(call) result = call #define GOOD_RESULT(call) if (result.good()) call #define BAD_RESULT(call) if (result.bad()) call // index positions in node list (makes source code more readable) #define MEASUREMENT_GROUP 0 #define ACTIVITY_SESSION 1 #define TRACKING_IDENTIFIER 2 #define TRACKING_UNIQUE_IDENTIFIER 3 #define FINDING 4 #define TIME_POINT 5 #define REFERENCED_SEGMENT 6 #define SOURCE_SERIES_FOR_SEGMENTATION 7 #define REAL_WORLD_VALUE_MAP 8 #define MEASUREMENT_METHOD 9 #define LAST_FINDING_SITE 10 #define LAST_MEASUREMENT 11 #define LAST_QUALITATIVE_EVALUATION 12 #define NUMBER_OF_LIST_ENTRIES 13 // general information on TID 1411 (Volumetric ROI Measurements) #define TEMPLATE_NUMBER "1411" #define MAPPING_RESOURCE "DCMR" #define MAPPING_RESOURCE_UID UID_DICOMContentMappingResource #define TEMPLATE_TYPE OFTrue /* extensible */ #define TEMPLATE_ORDER OFFalse /* non-significant */ template TID1411_VolumetricROIMeasurements::TID1411_VolumetricROIMeasurements(const OFBool createGroup) : DSRSubTemplate(TEMPLATE_NUMBER, MAPPING_RESOURCE, MAPPING_RESOURCE_UID), Measurement(new TID1419_Measurement()) { setExtensible(TEMPLATE_TYPE); setOrderSignificant(TEMPLATE_ORDER); /* need to store position of various content items */ reserveEntriesInNodeList(NUMBER_OF_LIST_ENTRIES, OFTrue /*initialize*/); /* TID 1411 (Volumetric ROI Measurements) Row 1 */ if (createGroup) createMeasurementGroup(); } template void TID1411_VolumetricROIMeasurements::clear() { DSRSubTemplate::clear(); Measurement->clear(); } template OFBool TID1411_VolumetricROIMeasurements::isValid() const { /* check whether base class is valid and all required content items are present */ return DSRSubTemplate::isValid() && hasMeasurementGroup() && hasReferencedSegment() && hasSourceSeriesForSegmentation(); } template OFBool TID1411_VolumetricROIMeasurements::hasMeasurementGroup(const OFBool checkChildren) const { OFBool result = OFFalse; /* need to check for child nodes? */ if (checkChildren) { DSRDocumentTreeNodeCursor cursor(getRoot()); /* go to content item at TID 1411 (Volumetric ROI Measurements) Row 1 */ if (gotoEntryFromNodeList(cursor, MEASUREMENT_GROUP) > 0) result = cursor.hasChildNodes(); } else { /* check for content item at TID 1411 (Volumetric ROI Measurements) Row 1 */ result = (getEntryFromNodeList(MEASUREMENT_GROUP) > 0); } return result; } template OFBool TID1411_VolumetricROIMeasurements::hasTrackingIdentifier() const { /* check for content item at TID 1411 (Volumetric ROI Measurements) Row 2 */ return (getEntryFromNodeList(TRACKING_IDENTIFIER) > 0); } template OFBool TID1411_VolumetricROIMeasurements::hasTrackingUniqueIdentifier() const { /* check for content item at TID 1411 (Volumetric ROI Measurements) Row 3 */ return (getEntryFromNodeList(TRACKING_UNIQUE_IDENTIFIER) > 0); } template OFBool TID1411_VolumetricROIMeasurements::hasReferencedSegment() const { /* check for content item at TID 1411 (Volumetric ROI Measurements) Row 7 */ return (getEntryFromNodeList(REFERENCED_SEGMENT) > 0); } template OFBool TID1411_VolumetricROIMeasurements::hasSourceSeriesForSegmentation() const { /* check for content item at TID 1411 (Volumetric ROI Measurements) Row 12 */ return (getEntryFromNodeList(SOURCE_SERIES_FOR_SEGMENTATION) > 0); } template OFBool TID1411_VolumetricROIMeasurements::hasMeasurements(const OFBool checkChildren) const { OFBool result = OFFalse; /* need to check for child nodes? */ if (checkChildren) { DSRDocumentTreeNodeCursor cursor(getRoot()); /* go to content item at TID 1411 (Volumetric ROI Measurements) Row 15 */ if (gotoEntryFromNodeList(cursor, LAST_MEASUREMENT) > 0) { /* check whether any of the "included TID 1419 templates" is non-empty */ while (cursor.isValid() && (cursor.getNode()->getValueType() == VT_includedTemplate)) { const DSRSubTemplate *subTempl = OFstatic_cast(const DSRIncludedTemplateTreeNode *, cursor.getNode())->getValue().get(); if (subTempl != NULL) { if (subTempl->compareTemplateIdentication("1419", "DCMR")) { result = !subTempl->isEmpty(); if (result) break; } else { /* exit loop */ break; } } if (cursor.gotoPrevious() == 0) { /* invalidate cursor */ cursor.clear(); } } } } else { /* check for content item at TID 1411 (Volumetric ROI Measurements) Row 13 */ result = (getEntryFromNodeList(LAST_MEASUREMENT) > 0); } return result; } template OFCondition TID1411_VolumetricROIMeasurements::setActivitySession(const OFString &session, const OFBool check) { OFCondition result = EC_Normal; /* basic check of parameter */ if (!session.empty()) { /* check whether measurement group already exists */ if (!hasMeasurementGroup()) result = createMeasurementGroup(); /* TID 1411 (Volumetric ROI Measurements) Row 1b */ CHECK_RESULT(addOrReplaceContentItem(ACTIVITY_SESSION, RT_hasObsContext, VT_Text, CODE_NCIt_ActivitySession, "TID 1411 - Row 1b", check)); CHECK_RESULT(getCurrentContentItem().setStringValue(session, check)); } else result = EC_IllegalParameter; return result; } template OFCondition TID1411_VolumetricROIMeasurements::setTrackingIdentifier(const OFString &trackingID, const OFBool check) { OFCondition result = EC_Normal; /* basic check of parameter */ if (!trackingID.empty()) { /* check whether measurement group already exists */ if (!hasMeasurementGroup()) result = createMeasurementGroup(); /* TID 1411 (Volumetric ROI Measurements) Row 2 */ CHECK_RESULT(addOrReplaceContentItem(TRACKING_IDENTIFIER, RT_hasObsContext, VT_Text, CODE_DCM_TrackingIdentifier, "TID 1411 - Row 2", check)); CHECK_RESULT(getCurrentContentItem().setStringValue(trackingID, check)); } else result = EC_IllegalParameter; return result; } template OFCondition TID1411_VolumetricROIMeasurements::setTrackingUniqueIdentifier(const OFString &trackingUID, const OFBool check) { OFCondition result = EC_Normal; /* basic check of parameter */ if (!trackingUID.empty()) { /* check whether measurement group already exists */ if (!hasMeasurementGroup()) result = createMeasurementGroup(); /* TID 1411 (Volumetric ROI Measurements) Row 3 */ CHECK_RESULT(addOrReplaceContentItem(TRACKING_UNIQUE_IDENTIFIER, RT_hasObsContext, VT_UIDRef, CODE_DCM_TrackingUniqueIdentifier, "TID 1411 - Row 3", check)); CHECK_RESULT(getCurrentContentItem().setStringValue(trackingUID, check)); } else result = EC_IllegalParameter; return result; } template OFCondition TID1411_VolumetricROIMeasurements::setFinding(const DSRCodedEntryValue &finding, const OFBool check) { OFCondition result = EC_Normal; /* basic check of parameter */ if (finding.isComplete()) { /* check whether measurement group already exists */ if (!hasMeasurementGroup()) result = createMeasurementGroup(); /* TID 1411 (Volumetric ROI Measurements) Row 3b */ CHECK_RESULT(addOrReplaceContentItem(FINDING, RT_contains, VT_Code, CODE_DCM_Finding, "TID 1411 - Row 3b", check)); CHECK_RESULT(getCurrentContentItem().setCodeValue(finding, check)); } else result = EC_IllegalParameter; return result; } template OFCondition TID1411_VolumetricROIMeasurements::setTimePoint(const OFString &timePoint, const OFBool check) { OFCondition result = EC_Normal; /* basic check of parameter */ if (!timePoint.empty()) { /* check whether measurement group already exists */ if (!hasMeasurementGroup()) result = createMeasurementGroup(); /* TID 1502 (Time Point Context) Row 3 */ CHECK_RESULT(addOrReplaceContentItem(TIME_POINT, RT_hasObsContext, VT_Text, CODE_UMLS_TimePoint, "TID 1502 - Row 3", check)); CHECK_RESULT(getCurrentContentItem().setStringValue(timePoint, check)); } else result = EC_IllegalParameter; return result; } template OFCondition TID1411_VolumetricROIMeasurements::setReferencedSegment(const DSRImageReferenceValue &segment, const OFBool check) { OFCondition result = EC_Normal; /* basic check of parameter */ if (segment.isComplete()) { const char *annotationText = "TID 1411 - Row 7"; const DSRBasicCodedEntry conceptName(CODE_DCM_ReferencedSegment); /* check for supported segmentation SOP classes */ if ((segment.getSOPClassUID() != UID_SegmentationStorage) && (segment.getSOPClassUID() != UID_SurfaceSegmentationStorage)) { DCMSR_CMR_WARN("Cannot set value of '" << conceptName.CodeMeaning << "' content item (" << annotationText << ") ... wrong SOP Class"); DCMSR_CMR_DEBUG("SOP Class UID \"" << segment.getSOPClassUID() << "\" does not match one of the known Segmentation objects"); result = CMR_EC_InvalidSegmentationObject; } /* ... and number of referenced segments */ else if ((segment.getSegmentList().getNumberOfItems() != 1)) { DCMSR_CMR_WARN("Cannot set value of '" << conceptName.CodeMeaning << "' content item (" << annotationText << ") ... wrong number of segments"); result = CMR_EC_InvalidSegmentationObject; } else { /* check whether measurement group already exists */ if (!hasMeasurementGroup()) result = createMeasurementGroup(); /* TID 1411 (Volumetric ROI Measurements) Row 7 */ CHECK_RESULT(addOrReplaceContentItem(REFERENCED_SEGMENT, RT_contains, VT_Image, conceptName, annotationText, check)); CHECK_RESULT(getCurrentContentItem().setImageReference(segment, check)); } } else result = EC_IllegalParameter; return result; } template OFCondition TID1411_VolumetricROIMeasurements::setReferencedSegment(DcmItem &dataset, const Uint16 segmentNumber, const OFBool copyTracking, const OFBool check) { DSRImageReferenceValue segment; /* first, create the referenced image/segment object */ OFCondition result = segment.setReference(dataset, check); segment.getSegmentList().addItem(segmentNumber); /* then, add/set the corresponding content item */ CHECK_RESULT(setReferencedSegment(segment, check)); /* need to copy tracking information? (introduced with CP-1496) */ if (copyTracking && result.good()) { DcmSequenceOfItems *dseq = NULL; /* get SegmentSequence (should always be present) */ result = dataset.findAndGetSequence(DCM_SegmentSequence, dseq); checkElementValue(dseq, DCM_SegmentSequence, "1-n", "1", result, "SegmentDescriptionMacro"); if (result.good()) { DcmObject *dobj = NULL; OFBool segmentFound = OFFalse; /* iterate over all items in this sequence */ while (((dobj = dseq->nextInContainer(dobj)) != NULL) && !segmentFound) { Uint16 number = 0; DcmItem *ditem = OFstatic_cast(DcmItem *, dobj); /* search for given segment number */ if (ditem->findAndGetUint16(DCM_SegmentNumber, number).good()) { if (segmentNumber == number) { OFString trackingID, trackingUID; /* get tracking ID and UID from current item (if present) and add/set content item */ getAndCheckStringValueFromDataset(*ditem, DCM_TrackingID, trackingID, "1", "1C", "SegmentSequence"); getAndCheckStringValueFromDataset(*ditem, DCM_TrackingUID, trackingUID, "1", "1C", "SegmentSequence"); if (!trackingID.empty() && !trackingUID.empty()) { CHECK_RESULT(setTrackingIdentifier(trackingID, check)); CHECK_RESULT(setTrackingUniqueIdentifier(trackingUID, check)); } else if (trackingID.empty() != trackingUID.empty()) { /* report a violation of the type 1C conditions */ DCMSR_CMR_WARN("Either Tracking ID or Tracking UID is absent/empty in referenced segmentation object"); } /* given segment number found */ segmentFound = OFTrue; } } } /* report a warning if referenced segment could not be found */ if (!segmentFound) { DCMSR_CMR_WARN("Cannot copy tracking information for '" << CODE_DCM_ReferencedSegment.CodeMeaning << "' content item (TID 1411 - Row 7) ... segment not found"); DCMSR_CMR_DEBUG("Cannot find given Segment Number (" << segmentNumber << ") in Segment Sequence of referenced segmentation object"); } } else { /* report a warning if referenced segment could not be found */ DCMSR_CMR_WARN("Cannot copy tracking information for '" << CODE_DCM_ReferencedSegment.CodeMeaning << "' content item (TID 1411 - Row 7) ... segment not found"); } /* tbc: return with an error in case the tracking information could not be copied? */ } return result; } template OFCondition TID1411_VolumetricROIMeasurements::setSourceSeriesForSegmentation(const OFString &seriesUID, const OFBool check) { OFCondition result = EC_Normal; /* basic check of parameter */ if (!seriesUID.empty()) { /* check whether measurement group already exists */ if (!hasMeasurementGroup()) result = createMeasurementGroup(); /* TID 1411 (Volumetric ROI Measurements) Row 12 */ CHECK_RESULT(addOrReplaceContentItem(SOURCE_SERIES_FOR_SEGMENTATION, RT_contains, VT_UIDRef, CODE_DCM_SourceSeriesForSegmentation, "TID 1411 - Row 12", check)); CHECK_RESULT(getCurrentContentItem().setStringValue(seriesUID, check)); } else result = EC_IllegalParameter; return result; } template OFCondition TID1411_VolumetricROIMeasurements::setRealWorldValueMap(const DSRCompositeReferenceValue &valueMap, const OFBool check) { OFCondition result = EC_Normal; /* basic check of parameter */ if (valueMap.isComplete()) { const char *annotationText = "TID 1411 - Row 14"; const DSRBasicCodedEntry conceptName(CODE_DCM_RealWorldValueMapUsedForMeasurement); /* check for real world value mapping SOP classes */ if (valueMap.getSOPClassUID() != UID_RealWorldValueMappingStorage) { DCMSR_CMR_WARN("Cannot set value of '" << conceptName.CodeMeaning << "' content item (" << annotationText << ") ... wrong SOP Class"); DCMSR_CMR_DEBUG("SOP Class UID \"" << valueMap.getSOPClassUID() << "\" does not match the one of the Real World Value Mapping object"); result = CMR_EC_InvalidRealWorldValueMappingObject; } else { /* check whether measurement group already exists */ if (!hasMeasurementGroup()) result = createMeasurementGroup(); /* TID 1411 (Volumetric ROI Measurements) Row 14 */ CHECK_RESULT(addOrReplaceContentItem(REAL_WORLD_VALUE_MAP, RT_contains, VT_Composite, conceptName, annotationText, check)); CHECK_RESULT(getCurrentContentItem().setCompositeReference(valueMap, check)); } } else result = EC_IllegalParameter; return result; } template OFCondition TID1411_VolumetricROIMeasurements::setRealWorldValueMap(DcmItem &dataset, const OFBool check) { DSRCompositeReferenceValue valueMap; /* first, create the referenced composite object */ OFCondition result = valueMap.setReference(dataset, check); /* then, add/set the corresponding content item */ CHECK_RESULT(setRealWorldValueMap(valueMap, check)); return result; } template OFCondition TID1411_VolumetricROIMeasurements::setMeasurementMethod(const T_Method &method, const OFBool check) { OFCondition result = EC_Normal; /* basic check of parameter */ if (method.hasSelectedValue()) { /* check whether measurement group already exists */ if (!hasMeasurementGroup()) result = createMeasurementGroup(); /* TID 1419 (ROI Measurements) Row 1 */ CHECK_RESULT(addOrReplaceContentItem(MEASUREMENT_METHOD, RT_hasConceptMod, VT_Code, CODE_SCT_MeasurementMethod, "TID 1419 - Row 1", check)); CHECK_RESULT(getCurrentContentItem().setCodeValue(method, check)); } else result = EC_IllegalParameter; return result; } template OFCondition TID1411_VolumetricROIMeasurements::addFindingSite(const DSRCodedEntryValue &site, const CID244e_Laterality &laterality, const DSRCodedEntryValue &siteModifier, const OFBool check) { OFCondition result = EC_Normal; /* basic check of mandatory parameter */ if (site.isComplete()) { /* check whether measurement group already exists */ if (!hasMeasurementGroup()) result = createMeasurementGroup(); if (result.good()) { /* create a new subtree in order to "rollback" in case of error */ DSRDocumentSubTree *subTree = new DSRDocumentSubTree; if (subTree != NULL) { /* TID 1419 (ROI Measurements) Row 2 */ CHECK_RESULT(subTree->addContentItem(RT_hasConceptMod, VT_Code, CODE_SCT_FindingSite, check)); CHECK_RESULT(subTree->getCurrentContentItem().setCodeValue(site, check)); CHECK_RESULT(subTree->getCurrentContentItem().setAnnotationText("TID 1419 - Row 2")); const size_t lastNode = subTree->getNodeID(); /* TID 1419 (ROI Measurements) Row 3 - optional */ if (laterality.hasSelectedValue()) { CHECK_RESULT(subTree->addChildContentItem(RT_hasConceptMod, VT_Code, CODE_SCT_Laterality, check)); CHECK_RESULT(subTree->getCurrentContentItem().setCodeValue(laterality, check)); CHECK_RESULT(subTree->getCurrentContentItem().setAnnotationText("TID 1419 - Row 3")); GOOD_RESULT(subTree->gotoParent()); } /* TID 1419 (ROI Measurements) Row 4 - optional */ if (siteModifier.isComplete()) { CHECK_RESULT(subTree->addChildContentItem(RT_hasConceptMod, VT_Code, CODE_SCT_TopographicalModifier, check)); CHECK_RESULT(subTree->getCurrentContentItem().setCodeValue(siteModifier, check)); CHECK_RESULT(subTree->getCurrentContentItem().setAnnotationText("TID 1419 - Row 4")); GOOD_RESULT(subTree->gotoParent()); } /* if everything was OK, insert new subtree into the template */ if (result.good() && !subTree->isEmpty()) { /* go to last measurement (if any) */ if (gotoLastEntryFromNodeList(this, LAST_FINDING_SITE) == getEntryFromNodeList(MEASUREMENT_GROUP)) { /* insert subtree below root */ STORE_RESULT(insertSubTree(subTree, AM_belowCurrent)); } else { /* insert subtree after current position */ STORE_RESULT(insertSubTree(subTree, AM_afterCurrent)); } /* store ID of recently added node for later use */ GOOD_RESULT(storeEntryInNodeList(LAST_FINDING_SITE, lastNode)); /* in case of error, make sure that memory is freed */ BAD_RESULT(delete subTree); } else { /* delete the new subtree since it has not been inserted */ delete subTree; } } else result = EC_MemoryExhausted; } else result = CMR_EC_NoMeasurement; } else result = EC_IllegalParameter; return result; } template OFCondition TID1411_VolumetricROIMeasurements::addMeasurement(const T_Measurement &conceptName, const MeasurementValue &numericValue, const OFBool checkEmpty, const OFBool checkValue) { OFCondition result = EC_Normal; /* basic check of mandatory parameters */ if (conceptName.hasSelectedValue() && numericValue.isComplete()) { /* check whether measurement group already exists */ if (!hasMeasurementGroup()) result = createMeasurementGroup(); if (result.good()) { /* go to content item at TID 1411 (Volumetric ROI Measurements) Row 15 */ if (gotoEntryFromNodeList(this, LAST_MEASUREMENT) > 0) { /* check whether the current instance of TID 1419 is non-empty (if needed) */ if (checkEmpty && Measurement->isEmpty()) result = getMeasurement().createNewMeasurement(conceptName, numericValue, checkValue); else { /* create new instance of TID 1419 (ROI Measurements) */ TID1419_Measurement *subTempl = new TID1419_Measurement(conceptName, numericValue, checkValue); if (subTempl != NULL) { /* store (shared) reference to new instance */ Measurement.reset(subTempl); /* and add it to the current template (TID 1411 - Row 15) */ STORE_RESULT(includeTemplate(Measurement, AM_afterCurrent, RT_contains)); CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1411 - Row 15")); GOOD_RESULT(storeEntryInNodeList(LAST_MEASUREMENT, getNodeID())); /* tbc: what if the call of includeTemplate() fails? */ } else result = EC_MemoryExhausted; } } else result = CMR_EC_NoMeasurementGroup; } } else result = EC_IllegalParameter; return result; } template OFCondition TID1411_VolumetricROIMeasurements::addQualitativeEvaluation(const DSRCodedEntryValue &conceptName, const DSRCodedEntryValue &codeValue, const OFBool check) { OFCondition result = EC_Normal; /* make sure that the parameters are non-empty */ if (conceptName.isComplete() && codeValue.isComplete()) { /* check whether measurement group already exists */ if (!hasMeasurementGroup()) result = createMeasurementGroup(); if (result.good()) { /* go to last qualitative evaluation (if any) */ if (gotoLastEntryFromNodeList(this, LAST_QUALITATIVE_EVALUATION) == getEntryFromNodeList(MEASUREMENT_GROUP)) { /* insert TID 1411 (Volumetric ROI Measurements) Row 16 below root */ STORE_RESULT(addChildContentItem(RT_contains, VT_Code, conceptName, check)); } else { /* insert TID 1411 (Volumetric ROI Measurements) Row 16 after current position */ STORE_RESULT(addContentItem(RT_contains, VT_Code, conceptName, check)); } CHECK_RESULT(getCurrentContentItem().setCodeValue(codeValue, check)); CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1411 - Row 16")); /* store ID of recently added node for later use */ GOOD_RESULT(storeEntryInNodeList(LAST_QUALITATIVE_EVALUATION, getNodeID())); } } else result = EC_IllegalParameter; return result; } template OFCondition TID1411_VolumetricROIMeasurements::addQualitativeEvaluation(const DSRCodedEntryValue &conceptName, const OFString &stringValue, const OFBool check) { OFCondition result = EC_Normal; /* make sure that the parameters are non-empty */ if (conceptName.isComplete() && !stringValue.empty()) { /* check whether measurement group already exists */ if (!hasMeasurementGroup()) result = createMeasurementGroup(); if (result.good()) { /* go to last qualitative evaluation (if any) */ if (gotoLastEntryFromNodeList(this, LAST_QUALITATIVE_EVALUATION) == getEntryFromNodeList(MEASUREMENT_GROUP)) { /* insert TID 1411 (Volumetric ROI Measurements) Row 17 below root */ STORE_RESULT(addChildContentItem(RT_contains, VT_Text, conceptName, check)); } else { /* insert TID 1411 (Volumetric ROI Measurements) Row 17 after current position */ STORE_RESULT(addContentItem(RT_contains, VT_Text, conceptName, check)); } CHECK_RESULT(getCurrentContentItem().setStringValue(stringValue, check)); CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1411 - Row 17")); /* store ID of recently added node for later use */ GOOD_RESULT(storeEntryInNodeList(LAST_QUALITATIVE_EVALUATION, getNodeID())); } } else result = EC_IllegalParameter; return result; } // protected methods template OFCondition TID1411_VolumetricROIMeasurements::createMeasurementGroup() { OFCondition result = SR_EC_InvalidTemplateStructure; if (isEmpty()) { /* TID 1411 (Volumetric ROI Measurements) Row 1 */ STORE_RESULT(addContentItem(RT_unknown, VT_Container, CODE_DCM_MeasurementGroup)); CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1411 - Row 1")); GOOD_RESULT(storeEntryInNodeList(MEASUREMENT_GROUP, getNodeID())); /* TID 1411 (Volumetric ROI Measurements) Row 15 */ CHECK_RESULT(includeTemplate(Measurement, AM_belowCurrent, RT_contains)); CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1411 - Row 15")); GOOD_RESULT(storeEntryInNodeList(LAST_MEASUREMENT, getNodeID())); /* if anything went wrong, clear the report */ BAD_RESULT(clear()); } return result; } template OFCondition TID1411_VolumetricROIMeasurements::addOrReplaceContentItem(const size_t nodePos, const E_RelationshipType relationshipType, const E_ValueType valueType, const DSRCodedEntryValue &conceptName, const OFString &annotationText, const OFBool check) { OFCondition result = EC_Normal; /* check concept name and coded entry value */ if (conceptName.isComplete()) { /* check whether content item already exists */ if (getEntryFromNodeList(nodePos) == 0) { /* if not, create the content item (at correct position) */ if (gotoLastEntryFromNodeList(this, nodePos) == getEntryFromNodeList(MEASUREMENT_GROUP)) { /* need to add the new content item as the first child */ if (addContentItem(relationshipType, valueType, AM_belowCurrentBeforeFirstChild) > 0) { if (getCurrentContentItem().setConceptName(conceptName, check).bad()) result = SR_EC_InvalidConceptName; } else result = SR_EC_CannotAddContentItem; } else { /* add new content item as a sibling (after the current one) */ STORE_RESULT(addContentItem(relationshipType, valueType, conceptName)); } /* store ID of added node for later use */ GOOD_RESULT(storeEntryInNodeList(nodePos, getNodeID())); } else if (gotoEntryFromNodeList(this, nodePos) > 0) { /* make sure that the value type of the existing content item is correct */ if (getCurrentContentItem().getValueType() != valueType) { DCMSR_CMR_WARN("Cannot replace value of '" << conceptName.getCodeMeaning() << "' content item (" << annotationText << ") ... wrong value type"); result = SR_EC_InvalidContentItem; } else if (getCurrentContentItem().getConceptName() != conceptName) { DCMSR_CMR_WARN("Cannot replace value of '" << conceptName.getCodeMeaning() << "' content item (" << annotationText << ") ... wrong concept name"); result = SR_EC_InvalidConceptName; } else { DCMSR_CMR_DEBUG("Replacing value of '" << conceptName.getCodeMeaning() << "' content item (" << annotationText << ")"); /* the actual replacing of the value is done by the caller of this method */ } } else result = SR_EC_InvalidTemplateStructure; /* finally, set annotation */ CHECK_RESULT(getCurrentContentItem().setAnnotationText(annotationText)); } else result = SR_EC_InvalidConceptName; return result; } // explicit template instantiation (needed for use in TID 1500) template class TID1411_VolumetricROIMeasurements;