ArcGIS PRO SDK Spatial Join documentation

552
10
06-03-2020 06:37 AM
JeffK
by
Occasional Contributor III

I am building an Add-in (converting a PyQt app that I wrote) that will need to do a couple of spatial joins and I have been searching for the documentation / reference/ examples of how to do a spatial join (intersect/within) but it seems elusive. 

Intersect returns a boolean; join does not include spatial, searching "spatial join intersect" or "spatial+analyst+spatial+join" in the Pro API reference isn't much help and just returns stuff for table joining.  Besides a blog post I found with an example, "spatial analyst" documentation seems non-existent.  I'm used to python and the ample docs that are easy to find by searching... where is the docs for spatial join operations if it exists, or is the preferred method of running something like a spatial join a python script and toolbox?  I'd like to keep the Add-in 'tools' in C# and in the solution, without having to maintain a toolbox.  Wondering if that is possible though.  Thanks in advance!

0 Kudos
10 Replies
RichRuh
Esri Regular Contributor

The Pro SDK doesn't support creating spatial joins.  Using geoprocessing/Python here remains your best bet.

--Rich

0 Kudos
NobbirAhmed
Esri Regular Contributor

Our public documentation, samples and snippets are on public Github:

Home · Esri/arcgis-pro-sdk Wiki · GitHub 

Once there, you'll see links for concept, snippets, and samples.

At the top you'll find a link for Samples - GitHub - Esri/arcgis-pro-sdk-community-samples: ArcGIS Pro SDK for Microsoft .NET Framework Communit... 

Clicking the link will take you to a public Github repo - if you clone the repo you'll get samples for all areas. Geoprocessing samples are in also there.

Any geoprocessing tool follows a regular pattern - you'll find the pattern in the snippets and samples - namely:

Please review that to refine your questions - I'll be able to take you from there  Thanks for the question.

0 Kudos
Wolf
by Esri Regular Contributor
Esri Regular Contributor

I can't tell from your question what kind of workflow you are looking for, but below is some sample code that uses a 'line geometry' to search through a polygon layer to find all polygons that are intersected by that line geometry.  Then using each intersecting polygon it uses the Intersection method to get the actual geometry resulting from the intersection between line and polygon geometry.  Maybe this will help you further, i pulled the code from an upcoming 2.6 sample.

try
{
var mapView = MapView.Active;
if (mapView == null) return;

// select the first line feature layer in the active map
// we use that line to find all
var lineLayer = mapView.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>()
.Where(lyr => lyr.ShapeType == esriGeometryType.esriGeometryPolyline).FirstOrDefault();
if (lineLayer == null)
{
MessageBox.Show("Line layer missing from scene");
return;
}
// select the polygon feature layer in the active map
var polygonLayer = mapView.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>()
.Where(lyr => lyr.ShapeType == esriGeometryType.esriGeometryPolygon).FirstOrDefault();
if (polygonLayer == null)
{
MessageBox.Show("Polygon layer missing from scene");
return;
}
_ = QueuedTask.Run(() =>
{
var selection = lineLayer.GetSelection();
if (selection.GetCount() == 0)
{
MessageBox.Show("No Line feature has been selected");
return;
}
Geometry lineGeometry = null;
using (var selCursor = selection.Search())
{
if (selCursor.MoveNext())
{
lineGeometry = (selCursor.Current as Feature).GetShape().Clone();
}
}
// define the spatial query filter to get all intersecting features from polygon layer
var spatialQuery = new SpatialQueryFilter()
{
FilterGeometry = lineGeometry,
SpatialRelationship = SpatialRelationship.Intersects
};
// Fine intersecting features
var rowCursor = polygonLayer.Search(spatialQuery);
while (rowCursor.MoveNext())
{
using (var feature = rowCursor.Current as Feature)
{
var geomInterectingPolygon = feature.GetShape().Clone();
var resultGeom = GeometryEngine.Instance.Intersection(geomInterectingPolygon,
lineGeometry);
if (resultGeom.IsEmpty)
{
MessageBox.Show("result geometry is empty");
return;
}
MessageBox.Show($@"Result geometry type: {resultGeom.GeometryType} pnt count: {resultGeom.PointCount}");
}
}
return;
});

}
catch (Exception ex)
{
MessageBox.Show($@"Error: {ex}");
}
0 Kudos
JeffK
by
Occasional Contributor III

Thanks for the replies- I haven't been able to find anything on performing a 'Spatial Join' in the Github Wiki -been there a lot for other functions, but it doesn't provide anything for spatial join (intersect, within, contains, etc.,) operations. I am not sure if Intersection works with points, but it does look promising.

My workflow is using a point (either from a geometry from the selected feature in our address table or from a lat/long entered into the form) that will be intersected with our Township/Range/Section featureclass, subdivision featureclass, and assessor featureclass to return the TRS, subdivision, block and property owner information.  This resulting attributes will be used to populate the attributes in the target featureclass (well permits). 

The Add-in is a form-based GUI for the technicians to perform CRUD operations on a featureclass that has a related table for attachments.  I'll try to get a screen grab of it.  It's purpose is to provide an easy way to manage the featureclass and the attachments, create permit applications (the featureclass is also part of a webmap for public viewing and will have a method of submitting applications), and create reports from the dataset for non-GIS savvy users.

Thank you again for the ideas and sample code!

0 Kudos
Wolf
by Esri Regular Contributor
Esri Regular Contributor

The snippet I sent should work fine for your workflow:

My workflow is using a point (either from a geometry from the selected feature in our address table or from a lat/long entered into the form) that will be intersected with our Township/Range/Section featureclass, subdivision featureclass, and assessor featureclass to return the TRS, subdivision, block and property owner information.  This resulting attributes will be used to populate the attributes in the target featureclass (well permits). 

If you use the snippet (above) with your input Point Geometry (either from a geometry from the selected feature in our address table or from a lat/long entered into the form) as the FilterGeometry in the SpatialQueryFilter the Search on each layer will give you all the features that contain that Point Geometry.   You can then simply use that feature to extract the attributes you need to update (or add) your well permit feature. 

0 Kudos
JeffK
by
Occasional Contributor III

Nice, Thank you!  I'll have to work my features into the sample and give it a test. 

0 Kudos
NobbirAhmed
Esri Regular Contributor

As of now, the snippets show how to run "any" geoprocessing tool with some examples. It says to get the syntax from documentation or from Python examples and process using MakeEnvironmentArray. 

Not only you - we have heard from many other users who are confused like you  We are working on documentation to provide C# examples for each tool in main tool documentation. 

For now, please get the tool syntax from the documentation. You can follow the pattern from tool doc with Python example. For example, Spatial Join's Python syntax is:

SpatialJoin(target_features, join_features, out_feature_class, {join_operation}, {join_type}, {field_mapping}, {match_option}, {search_radius}, {distance_field_name})

all values can be passed as string - bundle them in MakeEnvironmentArray and pass the array to ExecuteToolAsync. You'll find some examples in the snippets section.

We understand your confusion - we'll try to create c#-specific syntax as soon as possible. SDK team is working on it. 

0 Kudos
JeffK
by
Occasional Contributor III

Will do and thank you for the assist.  I'll have to work on it tomorrow morning but will post the final code for anyone else who stumbles upon this.

0 Kudos
JeffK
by
Occasional Contributor III

I finally got to test the intersect query this morning and it worked out for intersecting the point with the two featureclasses.  It was much simpler than I thought it would be...  Thank you all for the guidance!

AddressInfo _addr = new AddressInfo();

await QueuedTask.Run(() =>
{
using (Geodatabase CoopView = new Geodatabase(new DatabaseConnectionFile(new Uri(Properties.Settings.Default.CoopViewSDE))))
using (Geodatabase PlnView = new Geodatabase(new DatabaseConnectionFile(new Uri(Properties.Settings.Default.PlanningViewSDE))))
using (FeatureClass SubdivFCView = CoopView.OpenDataset<FeatureClass>("coopview.sde.subdivision"))
using (FeatureClass LCGridFCView = PlnView.OpenDataset<FeatureClass>("plancnty.sdeplncnty.LCAddressGrid"))
{
// define the spatial query filter to get all intersecting features from polygon layer
var spatialQuery = new SpatialQueryFilter()
{
FilterGeometry = mapPoint,
SpatialRelationship = SpatialRelationship.Intersects
};

// Get the intersecting Subdivisions features attributes
var addrowCursor = SubdivFCView.Search(spatialQuery);
while (addrowCursor.MoveNext())
{
using (var feature = addrowCursor.Current as Feature)
{
var geomInterectingPolygon = feature.GetShape().Clone();
var resultGeom = GeometryEngine.Instance.Intersection(geomInterectingPolygon, mapPoint);

if (!resultGeom.IsEmpty)
{
_addr.Subdivision = Convert.ToString(feature["SUB_NAME"]);
}
}
}

// Get the intersecting LCGRID feature attributes
var lcgridrowCursor = LCGridFCView.Search(spatialQuery);
while (lcgridrowCursor.MoveNext())
{
using (var feature = lcgridrowCursor.Current as Feature)
{
var geomInterectingPolygon = feature.GetShape().Clone();
var resultGeom = GeometryEngine.Instance.Intersection(geomInterectingPolygon, mapPoint);

if (!resultGeom.IsEmpty)
{
_addr.Township = Convert.ToString(feature["townshp"]);
_addr._Range = Convert.ToString(feature["range"]);
_addr._Section = Convert.ToString(feature["section"]);
}
}
}
}
});
}
catch (GeodatabaseFieldException fieldException)
{
// One of the fields in the where clause might not exist. There are multiple ways this can be handled:
// Handle error appropriately
}
catch (Exception exception)
{
// logger.Error(exception.Message);
}
return _addr;‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos