Skip to content

Commit

Permalink
DUI3-305 agis implement highlight for individual features e.g. from r…
Browse files Browse the repository at this point in the history
…eceive report (#3529)

* introduce ObjectID constructor

* adapt BasicBindings highlight to use ObjectId struct

* select individual features

* update missing tracker Items

* query features by ID for selection

* separate struct for MapMembers

* add comments

* formatting

* formatting

---------

Co-authored-by: Oğuzhan Koral <45078678+oguzhankoral@users.noreply.github.com>
  • Loading branch information
KatKatKateryna and oguzhankoral authored Jul 1, 2024
1 parent cbe19c7 commit 7d10c7f
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System.Reflection;
using ArcGIS.Core.Data;
using ArcGIS.Desktop.Framework.Threading.Tasks;
using ArcGIS.Desktop.Mapping;
using Speckle.Connectors.ArcGIS.HostApp;
using Speckle.Connectors.ArcGIS.Utils;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
Expand Down Expand Up @@ -58,7 +60,8 @@ public BasicConnectorBinding(DocumentModelStore store, ArcGISSettings settings,

public void RemoveModel(ModelCard model) => _store.RemoveModel(model);

public void HighlightObjects(List<string> objectIds) => HighlightObjectsOnView(objectIds);
public void HighlightObjects(List<string> objectIds) =>
HighlightObjectsOnView(objectIds.Select(x => new ObjectID(x)).ToList());

public void HighlightModel(string modelCardId)
{
Expand All @@ -69,16 +72,16 @@ public void HighlightModel(string modelCardId)
return;
}

var objectIds = new List<string>();
var objectIds = new List<ObjectID>();

if (model is SenderModelCard senderModelCard)
{
objectIds = senderModelCard.SendFilter.NotNull().GetObjectIds();
objectIds = senderModelCard.SendFilter.NotNull().GetObjectIds().Select(x => new ObjectID(x)).ToList();
}

if (model is ReceiverModelCard receiverModelCard)
{
objectIds = receiverModelCard.BakedObjectIds.NotNull();
objectIds = receiverModelCard.BakedObjectIds.NotNull().Select(x => new ObjectID(x)).ToList();
}

if (objectIds is null)
Expand All @@ -88,42 +91,42 @@ public void HighlightModel(string modelCardId)
HighlightObjectsOnView(objectIds);
}

private async void HighlightObjectsOnView(List<string> objectIds)
private async void HighlightObjectsOnView(List<ObjectID> objectIds)
{
MapView mapView = MapView.Active;

await QueuedTask
.Run(() =>
{
List<MapMember> mapMembers = GetMapMembers(objectIds, mapView);
List<MapMemberFeature> mapMembersFeatures = GetMapMembers(objectIds, mapView);
ClearSelectionInTOC();
ClearSelection();
SelectMapMembersInTOC(mapMembers);
SelectMapMembers(mapMembers);
SelectMapMembersInTOC(mapMembersFeatures);
SelectMapMembersAndFeatures(mapMembersFeatures);
mapView.ZoomToSelected();
})
.ConfigureAwait(false);
}

private List<MapMember> GetMapMembers(List<string> objectIds, MapView mapView)
private List<MapMemberFeature> GetMapMembers(List<ObjectID> objectIds, MapView mapView)
{
List<MapMember> mapMembers = new();
// find the layer on the map (from the objectID) and add the featureID is available
List<MapMemberFeature> mapMembersFeatures = new();

foreach (string objectId in objectIds)
foreach (ObjectID objectId in objectIds)
{
MapMember mapMember = mapView.Map.FindLayer(objectId);
MapMember mapMember = mapView.Map.FindLayer(objectId.MappedLayerURI, true);
if (mapMember is null)
{
mapMember = mapView.Map.FindStandaloneTable(objectId);
mapMember = mapView.Map.FindStandaloneTable(objectId.MappedLayerURI);
}
if (mapMember is null)
if (mapMember is not null)
{
continue;
MapMemberFeature mapMembersFeat = new(mapMember, objectId.FeatureId);
mapMembersFeatures.Add(mapMembersFeat);
}
mapMembers.Add(mapMember);
}

return mapMembers;
return mapMembersFeatures;
}

private void ClearSelection()
Expand All @@ -143,24 +146,39 @@ private void ClearSelectionInTOC()
MapView.Active.ClearTOCSelection();
}

private void SelectMapMembers(List<MapMember> mapMembers)
private void SelectMapMembersAndFeatures(List<MapMemberFeature> mapMembersFeatures)
{
foreach (var member in mapMembers)
foreach (MapMemberFeature mapMemberFeat in mapMembersFeatures)
{
MapMember member = mapMemberFeat.MapMember;
if (member is FeatureLayer layer)
{
layer.Select();
if (mapMemberFeat.FeatureId == null)
{
// select full layer if featureID not specified
layer.Select();
}
else
{
// query features by ID
var objectIDfield = layer.GetFeatureClass().GetDefinition().GetObjectIDField();

// FeatureID range starts from 0, but auto-assigned IDs in the layer start from 1
QueryFilter anotherQueryFilter = new() { WhereClause = $"{objectIDfield} = {mapMemberFeat.FeatureId + 1}" };
using (Selection onlyOneSelection = layer.Select(anotherQueryFilter, SelectionCombinationMethod.New)) { }
}
}
}
}

private void SelectMapMembersInTOC(List<MapMember> mapMembers)
private void SelectMapMembersInTOC(List<MapMemberFeature> mapMembersFeatures)
{
List<Layer> layers = new();
List<StandaloneTable> tables = new();

foreach (MapMember member in mapMembers)
foreach (MapMemberFeature mapMemberFeat in mapMembersFeatures)
{
MapMember member = mapMemberFeat.MapMember;
if (member is Layer layer)
{
if (member is not GroupLayer) // group layer selection clears other layers selection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Speckle.Core.Models.GraphTraversal;
using Speckle.Converters.ArcGIS3;
using RasterLayer = Objects.GIS.RasterLayer;
using Speckle.Connectors.ArcGIS.Utils;

namespace Speckle.Connectors.ArcGIS.Operations.Receive;

Expand Down Expand Up @@ -116,21 +117,33 @@ CancellationToken cancellationToken
else if (trackerItem.DatasetId == null)
{
results.Add(
new(Status.ERROR, trackerItem.Base, null, null, new ArgumentException("Unknown error: Dataset not created"))
new(
Status.ERROR,
trackerItem.Base,
null,
null,
new ArgumentException($"Unknown error: Dataset not created for {trackerItem.Base.speckle_type}")
)
);
}
else if (bakedMapMembers.TryGetValue(trackerItem.DatasetId, out MapMember? value))
{
// add layer and layer URI to tracker
trackerItem.AddConvertedMapMember(value);
trackerItem.AddLayerURI(value.URI);
conversionTracker[item.Key] = trackerItem; // not necessary atm, but needed if we use conversionTracker further
// only add a report item
AddResultsFromTracker(trackerItem, results);
}
else
{
// add layer and layer URI to tracker
// add layer to Map
MapMember mapMember = AddDatasetsToMap(trackerItem, createdLayerGroups);

// add layer and layer URI to tracker
trackerItem.AddConvertedMapMember(mapMember);
trackerItem.AddLayerURI(mapMember.URI);
conversionTracker[item.Key] = trackerItem;
conversionTracker[item.Key] = trackerItem; // not necessary atm, but needed if we use conversionTracker further

// add layer URI to bakedIds
bakedObjectIds.Add(trackerItem.MappedLayerURI == null ? "" : trackerItem.MappedLayerURI);
Expand All @@ -151,24 +164,45 @@ CancellationToken cancellationToken

private void AddResultsFromTracker(ObjectConversionTracker trackerItem, List<ReceiveConversionResult> results)
{
// prioritize individual hostAppGeometry type, if available:
if (trackerItem.HostAppGeom != null)
{
results.Add(
new(Status.SUCCESS, trackerItem.Base, trackerItem.MappedLayerURI, trackerItem.HostAppGeom.GetType().ToString())
);
}
else
if (trackerItem.MappedLayerURI == null) // should not happen
{
results.Add(
new(
Status.SUCCESS,
Status.ERROR,
trackerItem.Base,
trackerItem.MappedLayerURI,
trackerItem.HostAppMapMember?.GetType().ToString()
null,
null,
new ArgumentException($"Created Layer URI not found for {trackerItem.Base.speckle_type}")
)
);
}
else
{
// encode layer ID and ID of its feature in 1 object represented as string
ObjectID objectId = new(trackerItem.MappedLayerURI, trackerItem.DatasetRow);
if (trackerItem.HostAppGeom != null) // individual hostAppGeometry
{
results.Add(
new(
Status.SUCCESS,
trackerItem.Base,
objectId.ObjectIdToString(),
trackerItem.HostAppGeom.GetType().ToString()
)
);
}
else // hostApp Layers
{
results.Add(
new(
Status.SUCCESS,
trackerItem.Base,
objectId.ObjectIdToString(),
trackerItem.HostAppMapMember?.GetType().ToString()
)
);
}
}
}

private MapMember AddDatasetsToMap(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using ArcGIS.Desktop.Mapping;

namespace Speckle.Connectors.ArcGIS.Utils;

// bind together a layer object on the map, and auto-assigned ID if the specific feature
public readonly struct MapMemberFeature
{
public int? FeatureId { get; } // unique feature id (start from 0) of a feature in the layer
public MapMember MapMember { get; } // layer object on the Map

public MapMemberFeature(MapMember mapMember, int? featureId)
{
MapMember = mapMember;
FeatureId = featureId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
namespace Speckle.Connectors.ArcGIS.Utils;

// this struct is needed to be able to parse single-string value into IDs of both a layer, and it's individual feature
public struct ObjectID
{
private const string FEATURE_ID_SEPARATOR = "__speckleFeatureId__";
public string MappedLayerURI { get; } // unique ID of the layer on the map
public int? FeatureId { get; } // unique feature id (start from 0) of a feature in the layer

public ObjectID(string encodedId)
{
List<string> stringParts = encodedId.Split(FEATURE_ID_SEPARATOR).ToList();
MappedLayerURI = stringParts[0];
FeatureId = null;
if (stringParts.Count > 1)
{
FeatureId = Convert.ToInt32(stringParts[1]);
}
}

public ObjectID(string layerId, int? featureId)
{
MappedLayerURI = layerId;
FeatureId = featureId;
}

public readonly string ObjectIdToString()
{
if (FeatureId == null)
{
return $"{MappedLayerURI}";
}
else
{
return $"{MappedLayerURI}{FEATURE_ID_SEPARATOR}{FeatureId}";
}
}
}

0 comments on commit 7d10c7f

Please sign in to comment.