Draw a rectangle and gather coordinates from custom usercontrol within addin

1301
11
Jump to solution
08-10-2021 03:06 AM
SebastianKrings
Occasional Contributor

Hi,

I have an AddIn where the user gets a dialog to do some Stuff.
Within this dialog the user shall be able to click a button.

On Click the user shall be able to draw a rectangle directly on the map. After MouseRelease the coordinates of the drawn rectangle are gathered and shown in the dialog.

I found this sample which does almost what I want:

https://github.com/Esri/arcgis-pro-sdk-community-samples/tree/master/Map-Exploration/MapToolIdentify...

@Wolf : found another thread you told that you posted that sample. My you can help me out?

 

The problem with this and all other samples I found so far about MapTool or SketchTool is that they all are added directly to the AddIn-Tab. As said I want to be able to have the click inside on of my custom usercontrols. I tried adding the corresponding XML of the MapTool to my userControl (instead of button which is there as placeholder).

 

 

<UserControl x:Class="MyProject.MyControl"
[....]>

    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/MyProject;component\Application\Styles\Standard.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>
	<StackPanel Orientation="Horizontal">
		<Label Content="{x:Static resx:UiResources.DataSelection_SpatialFilter_Radio_Window}" />
		<Button
			Style="{DynamicResource Esri_Button}"
			Command="{Binding CoordinatesByRectangleCommand}"
			ToolTip="Draw Rectangle and gather the coordinates."/>

		<tool id="MapToolIdentifyWithDockpane_MapToolIdentify" caption="Identify Features" className="MapToolIdentifyWithDockpane" loadOnClick="true" smallImage="Images\GenericButtonRed16.png" largeImage="Images\GenericButtonRed32.png" condition="esri_mapping_mapPane" keytip="z2">
			<tooltip heading="Identify Features">
				Identify features on the current map using a circular sketch.
				<disabledText />
			</tooltip>
		</tool>
	</StackPanel>
</UserControl>

 

 

The thing is, that my VS cannot resolve symbol tool.

 

It is very difficult to find any documentary about MapTools so I am unsure whether it even is possible to call a MapTool like I wish here.

 

If not, maybe there is another way to achieve getting the coordinates from a button-click within my custom usercontrol?

 

Hopefully there are some helping minds.
Thanks.

0 Kudos
1 Solution

Accepted Solutions
Wolf
by Esri Regular Contributor
Esri Regular Contributor

I modified the MapToolIdentifyWithDockpane sample and added the selection (by rectangle) button on the same identify dockpane.  I attached the sample project, maybe this will be of help.  

Wolf_0-1628621191768.png

 

View solution in original post

0 Kudos
11 Replies
KrisCulin
Occasional Contributor

Add the MapTool to the add-in's Config.daml file but do not add it to the add-in tab.  Then in the click event of the button in your user control, set the active tool to your custom one.  There's a method, I believe on FrameworkApplication, where you can set the active tool.  This should allow the user to draw the rectangle and for you to retrieve the coordinates of that object.

I can't promise it will work but this is what I would personally try.  See what happens.

Kris

0 Kudos
SebastianKrings
Occasional Contributor

Thanks for the quick reply.
Unfortunately its not working. The tool is not loaded. I tried with another esri-tool which works.

I added my tool to the daml as follows based on the tool defined here:
https://github.com/Esri/arcgis-pro-sdk-community-samples/blob/master/Map-Exploration/MapToolIdentify...

 

<ArcGIS defaultAssembly="MyProject.dll" defaultNamespace="MyProject" xmlns="http://schemas.esri.com/DADF/Registry" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.esri.com/DADF/Registry file:///C:/Program%20Files/ArcGIS/Pro/bin/ArcGIS.Desktop.Framework.xsd">
  <AddInInfo id="{...}" version="1.0.0" desktopVersion="2.7.26828">
    <Description>Revision ...+</Description>
    <Image>Application\Images\Light\earthquake32.png</Image>
    <Author>Me Author</Author>
    <Company>Me Company</Company>
    <Date>06.08.2021</Date>
    <Subject>Framework</Subject>
    <!-- Note subject can be one or more of these topics:
                    Content, Framework, Editing, Geodatabase, Geometry, Geoprocessing, Layouts, Map Authoring, Map Exploration -->
  </AddInInfo>
  <conditions>
    <insertCondition [...]>
	</insertCondition>

  </conditions>
  <modules>
    <insertModule id="MyProject_Module" className="MyProjectModule" autoLoad="true" caption="MyProject-Modul">
      <!-- uncomment to have the control hosted on a separate tab-->
      <tabs>
        <tab id="My_Project_Tab1" caption="MyProject" keytip="MyProject">
          <group refID="My_Project_Group" />
        </tab>
      </tabs>
      <groups>
        <!-- comment this out if you have no controls on the Addin tab to avoid
              an empty group-->
        <group id="My_Project_Group" caption="MyProject" appearsOnAddInTab="false" keytip="MyProject">
          <!-- host controls within groups -->
          <button [...] />
        </group>
      </groups>
      <controls>
        <!-- add your controls here -->
        <button [...]
        </button>
        <tool id="MapToolIdentifyWithDockpane_MapToolIdentify" caption="Identify Features" className="MyProject.CoordinatesByRectangleMapTool" loadOnClick="true" smallImage="Images\GenericButtonRed16.png" largeImage="Images\GenericButtonRed32.png" keytip="z2">
	        <tooltip heading="Identify Features">
		        Identify features on the current map using a circular sketch.
		        <disabledText />
	        </tooltip>
        </tool>
      </controls>
      <dockPanes>
        <dockPane [...]>
        </dockPane>
      </dockPanes>
    </insertModule>
  </modules>
</ArcGIS>

 

This is the Tool I just placed between our usual code. This also is copied from the sample mentioned above but just renamed and relocated in namespace:

namespace MyProject
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;

    using ArcGIS.Core.Data;
    using ArcGIS.Core.Geometry;
    using ArcGIS.Desktop.Framework.Threading.Tasks;
    using ArcGIS.Desktop.Mapping;

    public class CoordinatesByRectangleMapTool : MapTool
    {
        public CoordinatesByRectangleMapTool()
        {
            IsSketchTool = true;
            SketchType = SketchGeometryType.Rectangle;
            SketchOutputMode = SketchOutputMode.Screen;
        }

        protected override Task OnToolActivateAsync(bool active)
        {
            return base.OnToolActivateAsync(active);
        }

        protected override async Task<bool> OnSketchCompleteAsync(Geometry geometry)
        {
            var mv = MapView.Active;
            await QueuedTask.Run(() =>
            {
                // Get the features that intersect the sketch geometry. 
                // getFeatures returns a dictionary of featurelayer and a list of Object ids for each
                Dictionary<BasicFeatureLayer, List<long>> featuresObjectIds = mv.GetFeatures(geometry);

                // go through all feature layers and do a spatial query to find features 
                foreach (var featOids in featuresObjectIds)
                {
                    var featLyr = featOids.Key;
                    var qf = new QueryFilter() { ObjectIDs = featOids.Value };
                    var rowCursor = featLyr.Search(qf);
                    while (rowCursor.MoveNext())
                    {
                        using (var feat = rowCursor.Current as Feature)
                        {
                            var listOID = new List<long> { feat.GetObjectID() };
                            var displayExp = String.Join(Environment.NewLine, featLyr.QueryDisplayExpressions(listOID.ToArray()));
                            //Module1.MapToolIdentifyDockpaneVM.AddToListOfFeatures($@"Layer: {featLyr.Name} obj id: {feat.GetObjectID()} Display Expression: {displayExp}");
                            //Access all field values
                            var count = feat.GetFields().Count();
                            for (int i = 0; i < count; i++)
                            {
                                var val = feat[i];
                                //TODO use the value(s)
                            }
                        }
                    }
                }
            });
            return true;
        }
    }
}


The usercontrol contains a button where the command is declared as follows:

this.CoordinatedByRectangleCommand = new GenericCommand(this.CoordinatedByRectangle);

 

The method then tries the following code, which a copied from the sample mentioned in this thread:
https://community.esri.com/t5/arcgis-pro-sdk-questions/how-do-i-activate-deactivate-a-maptool/td-p/8...

 

private void CoordinatedByRectangle()
{
	//CurrentTool is esri map explore
	var currentTool = FrameworkApplication.CurrentTool;
	var activeTool = FrameworkApplication.ActiveTool;
	
	//First approach
	var task = FrameworkApplication.SetCurrentToolAsync("MapToolIdentifyWithDockpane_MapToolIdentify");
	x.Wait();
	
	
	//CurrentTool still is esri map explore
	currentTool = FrameworkApplication.CurrentTool;
	activeTool = FrameworkApplication.ActiveTool;

	//Second approach
	ICommand ccmd = FrameworkApplication.GetPlugInWrapper("MapToolIdentifyWithDockpane_MapToolIdentify") as ICommand;
	if ((ccmd != null) && ccmd.CanExecute(null))    // --> CanExecute results to false
		ccmd.Execute(null);
		
		
	//CurrentTool still is esri map explore
	currentTool = FrameworkApplication.CurrentTool;
	activeTool = FrameworkApplication.ActiveTool;
}

 

In the second approach I can see while debugging that CanExecute results to false.
There seems to be something wrong in the way I am doing.
The daml-ID seems to be correct, because in ccmd I can see the custom tooltip defined in the daml of the tool. So basically the tool can be found, but not executed.

 

When I exchange the daml-ID of my custom tool with another one from esri, like "esri_mapping_selectByRectangleTool", then the tool is changed and I directly can draw a selecting rectangle on the map.

 

Any idea whats wrong with my tool?
Thank you.

0 Kudos
Wolf
by Esri Regular Contributor
Esri Regular Contributor

I modified the MapToolIdentifyWithDockpane sample and added the selection (by rectangle) button on the same identify dockpane.  I attached the sample project, maybe this will be of help.  

Wolf_0-1628621191768.png

 

0 Kudos
SebastianKrings
Occasional Contributor

You're totally amazing. This works great.
Thank you so much.

Do you even have an idea why the canExecute results to false?

I was wondering whether there would have been some documentation about such a thing or is it very uncommon?

0 Kudos
SebastianKrings
Occasional Contributor

Unfortunately its still not working. I thought it was because I ran your code, so it installed a second AddIn and "registered" the MapTool from your solution.
I put your command into place in my code. And the canExecute results true.

But it took the MapTool from your code to run. After I uninstalled your AddIn it is not executing anything.
I also renamed the damlId and all other occurences to "CoordinatesByRectangleMapTool_MapTool"
Seems like he is not finding my class.

In the daml the classname is given with
className="MyProject.CoordinatesByRectangleMapTool"

The class is with namespace MyProject and class and filename is "CoordinatesByRectangleMapTool".

I am missing something, but what? May you can see something? Thanks.

 

The problem was, that my Tools Class was in another project than the daml. Although I full qualified it. Even the class from your addin was found. Well, I dont get it, but after I moved the class into the main-project it worked.

0 Kudos
SebastianKrings
Occasional Contributor

The MapTool gets a geometry as parameter.

The geometry has no spatialReference and so the coordinates are screen-coordinates starting in the top left with 0/0.

How do I convert them into coordinates of a specific spatialReference (by given wkid)?

0 Kudos
SebastianKrings
Occasional Contributor

Ok guess I found also a solution that for but I am unsure if thats the correct way.

I changed SketchOutput to Map instead of Screen.
And then convert the geometries coordinates to the desired spatial Reference as follows:

protected override async Task<bool> OnSketchCompleteAsync(Geometry geometry)
{
	//Get Map-Coordinates
	var extend = geometry.Extent;
	var xMin = extend.XMin;
	var xMax = extend.XMax;
	var yMin = extend.YMin;
	var yMax = extend.YMax;

	//Create desired SpatialReference
	var displaySpatialReference = SpatialReferenceBuilder.CreateSpatialReference(31466);
	
	//Create MapPoints with desired SpatialReference from Map-Coordinates
	var topRight = MapPointBuilder.CreateMapPoint(xMax, yMax, displaySpatialReference);
	var bottomLeft = MapPointBuilder.CreateMapPoint(xMin, yMin, displaySpatialReference);
	
	//Gather new xMin, xMax, yMin, yMax
	var xMin2 = bottomLeft.X;
	var xMax2 = topRight.X;
	var yMin2 = bottomLeft.Y;
	var yMax2 = topRight.Y;

	//Put new Coordinates into dialog
	BasicFilterVm.Current.SpatialFilterWindowMaxHigh = yMax2;
	BasicFilterVm.Current.SpatialFilterWindowMinHigh = yMin2;
	BasicFilterVm.Current.SpatialFilterWindowMaxRight = xMax2;
	BasicFilterVm.Current.SpatialFilterWindowMinRight = xMin2;
	
	return true;
}

 

Is that fine or is there a better way to achieve that?

0 Kudos
Wolf
by Esri Regular Contributor
Esri Regular Contributor

You can change the SketchOutputMode in your tool to SketchOutputMode.Map.  This should get you the sketch geometry in the same spatial reference as your map.  

 

public MapToolIdentifyWithDockpane()
{
    IsSketchTool = true;
    SketchType = SketchGeometryType.Rectangle;
    SketchOutputMode = SketchOutputMode.Map;
}

 

 If you need to project your geometry using a different coordinate system you can use GeometryEngine:

 

var geomProjected = GeometryEngine.Instance.Project(geometry, SpatialReferences.WebMercator);
                        

 

 

SebastianKrings
Occasional Contributor

Works fine thx.
Yet another question.
How can I make the tool work only once?

So after using it once the previous or any default tool should be loaded.
If the user wants to use the tool again, she should click the button again.

0 Kudos