How to move Annotation with visible displacement?

416
2
08-04-2022 03:16 AM
DavidMrázek
Occasional Contributor II

Hello,

I am trying to create a code that will move Annotation visibly moved like in the original Move in ArcGIS Pro.

This is how I would imagine it:

My code only does this:

 

 

 

 enum TrackingState
    {
        NotTracking = 0,
        CanTrack,
        Tracking
    }
    internal class MapTool1 : MapTool
    {
        private string side;
        private long oid;
        private Geometry shape;
        private Point? _workingLocation;
        private static readonly object _lock = new object();
        private TrackingState _tracking = TrackingState.NotTracking;
        private CIMTextSymbol _symbol;
        private IDisposable _graphic;
        private Geometry _geometry;
        private Point? _lastPosition;
        private bool click = false;
        private DateTime firstClick;
        private int clickCount = 0;
        private DateTime secondClick;
        private double time;
        private DateTime clickTime;
        private double timeOfClick;
        private DateTime clicking;
       
        public MapTool1()
        {
            IsSketchTool = false;
            SketchType = SketchGeometryType.Point;
            SketchOutputMode = SketchOutputMode.Map;
        }

        private void DoubleClick()
        {
            if (secondClick.Ticks / TimeSpan.TicksPerMillisecond != 0 && clickCount > 0)
            {
                FrameworkApplication.SetCurrentToolAsync(null);
                clickCount = 0;
            }
        }
        private AnnotationLayer AlLayerAnno(string layerAnno)
        {
            MapView mapView = MapView.Active;
            AnnotationLayer alLayerAnno = mapView.Map.FindLayers(layerAnno).FirstOrDefault() as AnnotationLayer;
            if (alLayerAnno == null)
            {
                MessageBox.Show(@"Does not exist.");
                return null;
            }
            return alLayerAnno;
        }

        private Inspector Insp()
        {
            var insp = new Inspector();
            return insp;
        }

        private EditOperation Edit()
        {
            var edit = new EditOperation
            {
                Name = "Annotation move",
                SelectNewFeatures = false
            };
            return edit;
        }

        private QueryFilter Filter(long oid)
        {
            var filter = new QueryFilter
            {
                WhereClause = " OBJECTID = " + oid
            };
            return filter;
        }
        protected override Task OnToolDeactivateAsync(bool mapChange)
        {
            lock (_lock)
            {
                if (_graphic != null)
                {
                    _graphic.Dispose();
                    _graphic = null;
                }
            }
            return base.OnToolDeactivateAsync(mapChange);
        }
        private Selection AnnoSelected(AnnotationLayer alLayerAnno, QueryFilter filter)
        {
            var annoSelected = alLayerAnno.Select(filter);
            return annoSelected;
        }
        private double[] CoordinatesCenter(Envelope envelope)
        {
            var mapPointCenter = GeometryEngine.Instance.Centroid(envelope);
            double coordinatesX = mapPointCenter.X;
            double coordinatesY = mapPointCenter.Y;
            double[] array = { coordinatesX, coordinatesY };
            return array;
        }
        private AnnotationProperties AnnotationProperties()
        {
            const string layerAnno = "BodyAnno";
            var alLayerAnno = AlLayerAnno(layerAnno);
            var insp = Insp();
            insp.Load(alLayerAnno, oid);
            AnnotationProperties annoProperties = insp.GetAnnotationProperties();
            return annoProperties;
        }
        private CIMSymbolReference SymbolReference(AnnotationProperties annoProperties, Geometry moved)
        {
            annoProperties.Shape = moved;
            var cover = annoProperties.TextGraphic.Symbol.Symbol;
            var textSymbol = cover as CIMTextSymbol;
            _symbol = textSymbol;
            var symbolReference = _symbol.MakeSymbolReference();
            return symbolReference;
        }
        private double[] Coordinate(MapViewMouseEventArgs e, double coordinatesX, double coordinatesY)
        {
            var mapPoint = ActiveMapView.ClientToMap(e.ClientPoint);
            var xCoordinate = mapPoint.X;
            var x = xCoordinate - coordinatesX;
            var yCoordinate = mapPoint.Y;
            var y = yCoordinate - coordinatesY;
            double[] array = { x, y };
            return array;
        }
        protected override void OnToolMouseDown(MapViewMouseButtonEventArgs e)
        {
            if (e.ChangedButton == MouseButton.Left)
                e.Handled = true;
            clickTime = DateTime.Now;
        }
        protected async override void OnToolMouseUp(MapViewMouseButtonEventArgs e)
        {
            MapView mapView = MapView.Active;
            await QueuedTask.Run(() =>
            {
                if (shape == null)
                {
                    clicking = DateTime.Now;
                    timeOfClick = (clicking - clickTime).TotalMilliseconds;
                    if (click == false)
                    {
                        click = true;
                        firstClick = DateTime.Now;
                    }
                    else
                    {
                        click = false;
                        secondClick = DateTime.Now;
                    }

                    time = (firstClick - secondClick).TotalMilliseconds;
                    if (secondClick.Ticks / TimeSpan.TicksPerMillisecond < firstClick.Ticks / TimeSpan.TicksPerMillisecond)
                    {
                        time = (secondClick - firstClick).TotalMilliseconds;
                        if (time < 0 && timeOfClick < 500)
                        {
                            DoubleClick();
                        }
                    }
                    clickCount++;
                    if (time < 0 && time > -1000)
                    {
                        DoubleClick();
                    }

                    if (timeOfClick > 500)
                    {
                        MessageBox.Show("Annotation deos not selected!");
                        click = false;
                        clickCount = 0;
                    }
                    if (clickCount > 1)
                    {
                        clickCount = 0;
                    }
                    return;
                }
                var annoProperties = AnnotationProperties();
                var insp = Insp();
                const string layerAnno = "BodyAnno";
                var alLayerAnno = AlLayerAnno(layerAnno);
                insp.Load(alLayerAnno, oid);
                var edit = Edit();
                var envelope = shape.Extent;
                var array = CoordinatesCenter(envelope);
                var coordinatesX = array.First();
                var coordinatesY = array.Last();
                if (shape.GeometryType != GeometryType.GeometryBag)
                {
                    var mapPoint = ActiveMapView.ClientToMap(e.ClientPoint);
                    var xCoordinate = mapPoint.X;
                    var x = xCoordinate - coordinatesX;
                    var yCoordinate = mapPoint.Y;
                    var y = yCoordinate - coordinatesY;
                    if (side == "up" || side == "down")
                    {
                        var moved = GeometryEngine.Instance.Move(shape, x, 0);
                        annoProperties.Shape = moved;
                    }
                    else
                    {
                        var moved = GeometryEngine.Instance.Move(shape, 0, y);
                        annoProperties.Shape = moved;
                    }

                    clickCount = 0;
                    click = false;
                }
                insp.SetAnnotationProperties(annoProperties);
                edit.Modify(insp);
                edit.Execute();
                oid = 0;
                shape = null;
                mapView.Map.SetSelection(null);
                OnToolDeactivateAsync(true);
                lock (_lock)
                {
                    _tracking = TrackingState.NotTracking;
                    _geometry = null;
                }
            });
        }

        protected override Task HandleMouseDownAsync(MapViewMouseButtonEventArgs e)
        {
            return QueuedTask.Run(() =>
            {
                var mapPoint = ActiveMapView.ClientToMap(e.ClientPoint);
                MapView mapView = MapView.Active;
                const string layerAnno = "BodyAnno";
                var alLayerAnno = AlLayerAnno(layerAnno);
                var features = mapView.GetFeatures(mapPoint);
                if (features.ContainsKey(alLayerAnno))
                {
                    var tag = features.FirstOrDefault(t => t.Key == alLayerAnno);
                    var oidList = tag.Value;
                    oid = oidList[0];
                    var filter = Filter(oid);
                    var annotationFind = alLayerAnno.Search(filter);
                    while (annotationFind.MoveNext())
                    {
                        var actualRow = annotationFind.Current;
                        side = actualRow["SIDE"].ToString();
                    }
                    var annoProperties = AnnotationProperties();
                    AnnoSelected(alLayerAnno, filter);
                    shape = annoProperties.Shape;
                    var geometry = shape;
                    lock (_lock)
                    {
                        _workingLocation = null;
                        _geometry = geometry;
                        if (_graphic != null)
                            _graphic.Dispose();
                        _graphic = AddOverlay(geometry, _symbol.MakeSymbolReference());
                        _tracking = TrackingState.CanTrack;
                    }
                }
               });
        }
        protected override async void OnToolMouseMove(MapViewMouseEventArgs e)
        {
            lock (_lock)
            {
                if (_tracking == TrackingState.NotTracking)
                    return;
                if (_workingLocation.HasValue)
                {
                    _lastPosition = e.ClientPoint;
                    return;
                }
                _lastPosition = e.ClientPoint;
                _workingLocation = e.ClientPoint;
                _tracking = TrackingState.Tracking;
            }

            await QueuedTask.Run(() =>
            {
                var annoProperties = AnnotationProperties();
                while (true)
                {
                    Point? point;
                    Geometry geometry = null;
                    IDisposable graphic;
                    lock (_lock)
                    {
                        point = _lastPosition;
                        _lastPosition = null;
                        _workingLocation = point;
                        if (point == null || !point.HasValue)
                        {
                            _workingLocation = null;
                            break;
                        }
                        if (_geometry == null || _graphic == null)
                        {
                            _tracking = TrackingState.NotTracking;
                            break;
                        }
                        graphic = _graphic;
                    }
                    var envelope = shape.Extent;
                    var array = CoordinatesCenter(envelope);
                    var coordinatesX = array.First();
                    var coordinatesY = array.Last();
                    if (shape.GeometryType != GeometryType.GeometryBag)
                    {
                        var arrayCoordinate = Coordinate(e, coordinatesX, coordinatesY);
                        var x = arrayCoordinate.First();
                        var y = arrayCoordinate.Last();
                        if (side == "up" || side == "down")
                        {
                            var moved = GeometryEngine.Instance.Move(shape, x, 0);
                            var symbolReference = SymbolReference(annoProperties, moved);
                            UpdateOverlay(graphic, moved, symbolReference);
                        }
                        else
                        {
                            var moved = GeometryEngine.Instance.Move(shape, 0, y);
                            var symbolReference = SymbolReference(annoProperties, moved);
                            UpdateOverlay(graphic, moved, symbolReference);
                        }
                    }
                }
            });
        }

 

Sorry for the quality of the video, my program broke so I had to use mobile.

Thanks for the tips.

David

 

0 Kudos
2 Replies
Wolf
by Esri Regular Contributor
Esri Regular Contributor

I think to duplicate this functionality will be a lot of work.  So why can't you use the built-in tool:

ICommand cmd = FrameworkApplication.GetPlugInWrapper("esri_editing_EditVerticesMove") as ICommand;
if ((cmd != null) && cmd.CanExecute(null))
  cmd.Execute(null);
0 Kudos
NarelleChedzey
Esri Contributor

Hi David, 

 

Sorry for the late reply.  I have been looking at your post and code and have a few comments.  

1.  I'm sure you've read the documentation we have about editing annotation and it's special considerations but am including a link here for reference.  (https://github.com/Esri/arcgis-pro-sdk/wiki/ProConcepts-Editing-Annotation

2.  The API currently doesn't expose the ability to show the yellow "grabhandle" that you see with the built in Move tool (that identifies the feature being moved).  We have identified this as future functionality we would like to add but currently do not have a timeline identified.

3.  The MapTool.AddOverlay function that you are using will create a graphic from the geometry and symbol parameters that are passed.  This is why you see the line being displayed on the screen.   The line is the baseline geometry  of the annotation feature - the geometry that the annotation text lies on.     To see the annotation text on the overlay,  you should use the AddOverlay function that takes a CIMGraphic  https://pro.arcgis.com/en/pro-app/latest/sdk/api-reference/topic27604.html.    The CIMGraphic passed would the CIMTextGraphic of the annotation feature  (found from AnnotationProperties.TextGraphic). 

4.  Because you're dealing with annotation and text, it is also important to use the second parameter of that AddOverlay method - the referenceScale.  This can be retrieved from the AnnotationFeatureClassDefinition. 

await QueuedTask.Run(() =>
{
  var annoFC = _layerAnno.GetFeatureClass() as AnnotationFeatureClass;
  var annoFCDef = annoFC.GetDefinition() as AnnotationFeatureClassDefinition;

  _annoRefScale = annoFCDef.GetReferenceScale();
  var refScaleUnits = annoFCDef.GetReferenceScaleUnits();
});

 

So the AddOverlay routine would become 

   _graphic = AddOverlay(_trackedTextGraphic, _annoRefScale);

 

5.  Unfortunately there is no MapTool.UpdateOverlay method that supports the moving of text graphics in the manner used in your code.  This is a gap in our API that was found as a result of looking at your post and it will be fixed in the next Pro release.   Thanks for helping to identify this. 

6. In the meantime a work around would be to dispose of the previous graphic and create a new one with the CIMTextGraphic at the moved geometry position.  Unfortunately because this is in  MouseMove, the performance may not be as smooth as the standard tool.  Here's a sample of code that could replace your UpdateOverlay call

Geometry movedGeometry = null;
if (_trackedSide == "up" || _trackedSide == "down")
{
  movedGeometry = GeometryEngine.Instance.Move(_trackedGeometry, x, 0);
}
else
{
  movedGeometry = GeometryEngine.Instance.Move(_trackedGeometry, 0, y);
}

lock (_lock)
{
  if (_graphic != null)
    _graphic.Dispose();

  var newGraphic = _trackedTextGraphic.Clone();
  newGraphic.Shape = movedGeometry;
  _graphic = AddOverlay(newGraphic, _annoRefScale);
}

 

I hope this provides some clarification and helps you make progress with your tool.  Please let me know if you have any questions or comments about the above.  

Regards

Narelle