public static AimTemplateTree AimAnnotationToAimTemplateTree(Aim4AnnotationInstance annotationInstance, AimTemplateTree template)
        {
            if (annotationInstance == null || annotationInstance.AimAnnotationEntity == null)
                return null;

            var annotation = annotationInstance.AimAnnotationEntity;
            if (annotation.ImagingPhysicalEntityCollection != null)
            {
                foreach (var imgPhysEnt in annotation.ImagingPhysicalEntityCollection)
                {
                    if (IsNullCodeList(imgPhysEnt.TypeCode))
                        continue;

                    AimTemplateTreeAnatomicEntityNode matchingTreeNode = FromImagingPhysicalEntity(imgPhysEnt, template);
                    if (matchingTreeNode == null)
                        return null;
                }
            }
            if (annotation.ImagingObservationEntityCollection != null)
            {
                foreach (var io in annotation.ImagingObservationEntityCollection)
                {
                    if (IsNullCodeList(io.TypeCode))
                        continue;

                    AimTemplateTreeImagingObservationNode matchingTreeNode = FromImagingObservationEntity(io, template);
                    if (matchingTreeNode == null)
                        return null;
                }
            }
            if (annotation.CalculationEntityCollection != null)
            {
                //foreach (var calc in annotation.CalculationCollection)
                //{
                //    AimTemplateTreeCalculationNode matchingTreeNode =
                //        template.TemplateNodes.OfType<AimTemplateTreeCalculationNode>().FirstOrDefault(
                //            treeCalc => treeCalc.Label == calc.Description);
                //    if (matchingTreeNode != null)
                //    {
                //        AimTemplateTreeCalculationType matchingCalculationType =
                //            matchingTreeNode.CalculationTypes.FirstOrDefault(
                //                treeCalc => ToStandardCodeSequence(treeCalc).CodeValue == calc.CodeValue);
                //        if (matchingCalculationType != null)
                //        {
                //            matchingTreeNode.SelectedCalculationType = matchingCalculationType;
                //        }
                //        else
                //            return null;

                //        //if (matchingTreeNode.HasConfidence && calc..HasValue)
                //        //    matchingTreeNode.ConfidenceValue = calc.AnnotatorConfidence.Value;
                //    }
                //    else
                //        return null;
                //}
            }
            if (annotation.InferenceEntityCollection != null)
            {
                foreach (var inference in annotation.InferenceEntityCollection)
                {
                    if (IsNullCodeList(inference.TypeCode))
                        continue;

                    foreach (var inferenceNode in template.TemplateNodes.OfType<AimTemplateTreeInferenceNode>())
                    {
                        AimTemplateTreeAllowedTerm matchingAllowedTerm =
                            inferenceNode.AllowedTerms.FirstOrDefault(term => DoTheyMatch(term, inference.TypeCode));
                        if (matchingAllowedTerm != null)
                            matchingAllowedTerm.Selected = true;
                    }
                }
            }
            template.Markup.Clear();
            if (annotation is aim4_dotnet.ImageAnnotation)
            {
                var imageAnnotation = (aim4_dotnet.ImageAnnotation)annotation;
                var annotationName = string.IsNullOrEmpty(imageAnnotation.Name) ? "" : imageAnnotation.Name;

                if (imageAnnotation.MarkupEntityCollection != null)
                {
                    foreach (var markupEntity in imageAnnotation.MarkupEntityCollection)
                    {
                        if (markupEntity is aim4_dotnet.GeometricShapeEntity)
                        {
                            var markup = ToMarkup2D((aim4_dotnet.GeometricShapeEntity)markupEntity, annotationName);
                            if (markup != null)
                                template.Markup.Add(markup);
                        }
                        else if (markupEntity is aim4_dotnet.TextAnnotationEntity)
                        {
                            var textAnnotationEntity = (aim4_dotnet.TextAnnotationEntity) markupEntity;
                            if (textAnnotationEntity.GeometricShapeEntity != null)
                            {
                                var calloutText = AimHelpers.FormatPointCalloutText(annotationName, textAnnotationEntity.Text);
                                aim4_dotnet.TwoDimensionGeometricShapeEntity geoShapeEntity = null;
                                if (textAnnotationEntity.GeometricShapeEntity is aim4_dotnet.TwoDimensionGeometricShapeEntity)
                                {
                                    var twoDGeoShape = (aim4_dotnet.TwoDimensionGeometricShapeEntity) textAnnotationEntity.GeometricShapeEntity;
                                    switch (twoDGeoShape.TwoDimensionSpatialCoordinateCollection.Count)
                                    {
                                        case 1:
                                            {
                                                var markup = new MarkupPoint();
                                                markup.Name = annotationName;
                                                markup.IncludeInAnnotation = twoDGeoShape.IncludeFlag;
                                                markup.Point = AsPointF(twoDGeoShape.TwoDimensionSpatialCoordinateCollection[0]);
                                                markup.CalloutText = calloutText;
                                                markup.CalloutLocation = new PointF(markup.Point.X, markup.Point.Y);
                                                SetMarkupImageReference(markup, twoDGeoShape);
                                                markup.UseCrosshair = AimSettings.Default.UseCrosshairsForTextCallouts;
                                                template.Markup.Add(markup);
                                            }
                                            break;
                                        case 2:
                                            {
                                                var markup = new MarkupPoint();
                                                markup.Name = annotationName;
                                                markup.IncludeInAnnotation = twoDGeoShape.IncludeFlag;
                                                markup.Point = AsPointF(twoDGeoShape.TwoDimensionSpatialCoordinateCollection[0]);
                                                markup.CalloutText = calloutText;
                                                markup.CalloutLocation = AsPointF(twoDGeoShape.TwoDimensionSpatialCoordinateCollection[1]);
                                                SetMarkupImageReference(markup, twoDGeoShape);
                                                markup.UseCrosshair = AimSettings.Default.UseCrosshairsForTextCallouts;
                                                template.Markup.Add(markup);
                                            }
                                            break;
                                        default:
                                            Platform.Log(LogLevel.Error, "TextAnnotation has [{0}] Connector Points", twoDGeoShape.TwoDimensionSpatialCoordinateCollection.Count);
                                            break;
                                    }
                                }
                                else if (textAnnotationEntity.GeometricShapeEntity is aim4_dotnet.ThreeDimensionGeometricShapeEntity)
                                {
                                    // TODO: implement 2D markup conversion!
                                    // TODO: convert to the 2D ImageReferenceUid and referencedFrameNumber!

                                    var threeDGeoShape = (aim4_dotnet.ThreeDimensionGeometricShapeEntity) textAnnotationEntity.GeometricShapeEntity;
                                    switch (threeDGeoShape.ThreeDimensionSpatialCoordinateCollection.Count)
                                    {
                                        case 1:
                                            throw new NotImplementedException();
                                            break;
                                        case 2:
                                            throw new NotImplementedException();
                                            break;
                                        default:
                                            Platform.Log(LogLevel.Error, "TextAnnotation has [{0}] 3D Connector Points", threeDGeoShape.ThreeDimensionSpatialCoordinateCollection.Count);
                                            break;
                                    }
                                }
                            }
                        }
                        else
                            Debug.Assert(false, "Unexpected markup type");
                    }
                }
            }
            return template;
        }
        public static IMarkup ToMarkup2D(aim4_dotnet.GeometricShapeEntity geometricShape, string markupText)
        {
            if (geometricShape == null)
                return null;

            if (geometricShape is aim4_dotnet.TwoDimensionPoint)
            {
                var point = (aim4_dotnet.TwoDimensionPoint)geometricShape;

                var markup = new MarkupPoint();
                markup.Name = markupText;
                markup.IncludeInAnnotation = point.IncludeFlag;
                markup.Point = AsPointF(point.Center);
                markup.CalloutText = string.Empty; // markupText;
                markup.CalloutLocation = markup.Point - new SizeF(30, 30);
                SetMarkupImageReference(markup, point);

                return markup;
            }
            if (geometricShape is aim4_dotnet.TwoDimensionCircle)
            {
                var circle = (aim4_dotnet.TwoDimensionCircle)geometricShape;

                PointF centerPt = AsPointF(circle.Center);
                PointF radiusPt = AsPointF(circle.RadiusPoint);
                double radiusLength = Vector.Distance(centerPt, radiusPt);

                var markup = new MarkupEllipse();
                markup.Name = markupText;
                markup.IncludeInAnnotation = circle.IncludeFlag;
                markup.TopLeft = new PointF((float)(centerPt.X - radiusLength), (float)(centerPt.Y - radiusLength));
                markup.BottomRight = new PointF((float)(centerPt.X + radiusLength), (float)(centerPt.Y + radiusLength));
                markup.CalloutLocation = markup.TopLeft - new SizeF(30, 30);
                SetMarkupImageReference(markup, circle);

                return markup;
            }
            if (geometricShape is aim4_dotnet.TwoDimensionEllipse)
            {
                var ellipse = (aim4_dotnet.TwoDimensionEllipse)geometricShape;

                Debug.Assert(ellipse.TwoDimensionSpatialCoordinateCollection.Count == 4, "Ellipse must have four points");
                var firstMajorAxisPt = AsPointF(ellipse.TwoDimensionSpatialCoordinateCollection[0]);
                var secondMajorAxisPt = AsPointF(ellipse.TwoDimensionSpatialCoordinateCollection[1]);
                var firstMinorAxisPt = AsPointF(ellipse.TwoDimensionSpatialCoordinateCollection[2]);
                var secondMinorAxisPt = AsPointF(ellipse.TwoDimensionSpatialCoordinateCollection[3]);

                var markup = new MarkupEllipse();
                markup.Name = markupText;
                markup.IncludeInAnnotation = ellipse.IncludeFlag;
                markup.TopLeft = new PointF(firstMajorAxisPt.X, firstMinorAxisPt.Y);
                markup.BottomRight = new PointF(secondMajorAxisPt.X, secondMinorAxisPt.Y);
                markup.CalloutLocation = markup.TopLeft - new SizeF(30, 30);
                SetMarkupImageReference(markup, ellipse);

                return markup;
            }
            if (geometricShape is aim4_dotnet.TwoDimensionPolyline)
            {
                var polyline = (aim4_dotnet.TwoDimensionPolyline)geometricShape;

                // Check if this is a non-rotated rectangle
                if (polyline.TwoDimensionSpatialCoordinateCollection.Count == 4)
                {
                    PointF twoDPoint1 = AsPointF(polyline.TwoDimensionSpatialCoordinateCollection[0]);
                    PointF twoDPoint2 = AsPointF(polyline.TwoDimensionSpatialCoordinateCollection[1]);
                    PointF twoDPoint3 = AsPointF(polyline.TwoDimensionSpatialCoordinateCollection[2]);
                    PointF twoDPoint4 = AsPointF(polyline.TwoDimensionSpatialCoordinateCollection[3]);
                    // If it's a rectangle with sides parallel to the axes
                    if ((twoDPoint1.X == twoDPoint2.X && twoDPoint2.Y == twoDPoint3.Y && twoDPoint3.X == twoDPoint4.X && twoDPoint4.Y == twoDPoint1.Y) ||
                        (twoDPoint1.Y == twoDPoint2.Y && twoDPoint2.X == twoDPoint3.X && twoDPoint3.Y == twoDPoint4.Y && twoDPoint4.X == twoDPoint1.X))
                    {
                        var markupRectangle = new MarkupRectangle();
                        markupRectangle.TopLeft = twoDPoint1;
                        markupRectangle.BottomRight = twoDPoint3;
                        markupRectangle.Name = markupText;
                        markupRectangle.IncludeInAnnotation = polyline.IncludeFlag;
                        markupRectangle.CalloutLocation = markupRectangle.TopLeft - new SizeF(30, 30);
                        SetMarkupImageReference(markupRectangle, polyline);

                        return markupRectangle;
                    }
                }

                var points = new List<PointF>();
                foreach (var spatialCoordinate in polyline.TwoDimensionSpatialCoordinateCollection)
                    if (spatialCoordinate != null)
                        points.Add(AsPointF(spatialCoordinate));

                var markup = new MarkupPolygonal();
                markup.Vertices = points;
                markup.Name = markupText;
                markup.IncludeInAnnotation = polyline.IncludeFlag;
                markup.CalloutLocation = markup.Vertices[0] - new SizeF(30, 30);
                SetMarkupImageReference(markup, polyline);

                return markup;
            }
            if (geometricShape is aim4_dotnet.TwoDimensionMultiPoint)
            {
                var multiPoint = (aim4_dotnet.TwoDimensionMultiPoint)geometricShape;

                IMarkup markup;
                switch (multiPoint.TwoDimensionSpatialCoordinateCollection.Count)
                {
                    case 2:
                        {
                            // Line
                            var markupLinear = new MarkupLinear();
                            markupLinear.Vertices = new List<PointF>(2);
                            markupLinear.Vertices.Add(AsPointF(multiPoint.TwoDimensionSpatialCoordinateCollection[0]));
                            markupLinear.Vertices.Add(AsPointF(multiPoint.TwoDimensionSpatialCoordinateCollection[1]));
                            markupLinear.CalloutLocation = markupLinear.Vertices[0] - new SizeF(30, 30);
                            markup = markupLinear;
                        }
                        break;
                    case 3:
                        {
                            // Protractor
                            var markupProtractor = new MarkupProtractor();
                            markupProtractor.Points = new List<PointF>(3);
                            markupProtractor.Points.Add(AsPointF(multiPoint.TwoDimensionSpatialCoordinateCollection[0]));
                            markupProtractor.Points.Add(AsPointF(multiPoint.TwoDimensionSpatialCoordinateCollection[1]));
                            markupProtractor.Points.Add(AsPointF(multiPoint.TwoDimensionSpatialCoordinateCollection[2]));
                            markupProtractor.CalloutLocation = markupProtractor.Points[0] - new SizeF(30, 30);
                            markup = markupProtractor;
                        }
                        break;
                    default:
                        throw new NotImplementedException("Reading non-linear or non-triangular MultiPoint shape is not implemented");
                }

                markup.Name = markupText;
                markup.IncludeInAnnotation = multiPoint.IncludeFlag;
                SetMarkupImageReference(markup, multiPoint);
                return markup;
            }

            // TODO - 3D GeoShapes Conversions

            throw new NotImplementedException();
            return null;
        }