Long and short: Trying to add 100+ Graphics to a GraphicsOverlay takes like 10 seconds. Changing from "each new Graphic gets a Symbol" to "The GraphicsOverlay uses a SimpleRenderer, and each Graphic gets its Symbol from the Renderer" resulted in marginal improvement at best. Are we doing this incorrectly?
This is a followup from https://community.esri.com/t5/net-maps-sdk-questions/adding-a-large-number-of-graphics-in-a-mvvm/m-p/710704#M8947
In our situation, we have a ViewModel that sits in a left-sidebar, and the rest of the application window is the map. The left sidebar holds all of the visible features on the map (limited to ~100) in a Vertical Listview. Each feature is Selectable, and when the feature is Selected, the feature in the Map is "highlighted" by drawing a Symbol over the feature.
On the map, we have a many features - often several thousand at a 25000:1 scale. These features are nested in 3 layers of Grandparent > Parent > Child relationships - unrelated to this issue, aside from a recursive selection method. These features can be Polyline, Point, MultiPoint, Envelope, Polygon.
Our sidebar has a Select All/None button, self explanatory. However, this involves us adding/removing 100+ graphics w/ symbols to/from the mapview's graphics overlays. As described at the top, this action is taking 8-10s when executed.
Switching to using a SimpleRenderer didn't provide noticeable improvements in execution. What are we doing wrong?
Our code:
// This guy gets fired on ViewModel instantiation
private void InitGraphicsLayers()
{
// `this.Engine` is just a DI wrapper class that holds the ArcGISRuntime MapView
this.pointGraphicsOverlay = this.Engine.MapView.GraphicsOverlays.FirstOrDefault(g => g.Id.Equals(PointGraphicsOverlayId));
if (this.pointGraphicsOverlay is null)
{
// straightforward, a Cyan circle over the symbology of the Point geometry
Symbol pointSymbol = new SimpleMarkerSymbol
{
Color = System.Drawing.Color.Cyan,
Style = SimpleMarkerSymbolStyle.X,
Size = 15d,
};
SimpleRenderer renderer = new(pointSymbol);
// we have a couple of constants in this class so we can always
// create and find our overlays in `this.Engine.MapView.GraphicsOverlays`
this.pointGraphicsOverlay = new GraphicsOverlay() { Id = PointGraphicsOverlayId, Renderer = renderer, };
this.Engine.MapView.GraphicsOverlays.Add(this.pointGraphicsOverlay);
}
this.polyGraphicsOverlay = this.Engine.MapView.GraphicsOverlays.FirstOrDefault(g => g.Id.Equals(PolyGraphicsOverlayId));
if (this.polyGraphicsOverlay is null)
{
// dashed cyan line, used for Line geometries as well
Symbol polySymbol = new SimpleFillSymbol
{
Color = System.Drawing.Color.Cyan,
Style = SimpleFillSymbolStyle.Null,
Outline = new SimpleLineSymbol
{
Color = System.Drawing.Color.Cyan,
Style = SimpleLineSymbolStyle.Dash,
Width = 5d,
},
};
SimpleRenderer renderer = new(polySymbol);
this.polyGraphicsOverlay = new GraphicsOverlay() { Id = PolyGraphicsOverlayId, Renderer = renderer, };
this.Engine.MapView.GraphicsOverlays.Add(this.polyGraphicsOverlay);
}
this.lineGraphicsOverlay = this.Engine.MapView.GraphicsOverlays.FirstOrDefault(g => g.Id.Equals(LineGraphicsOverlayId));
if (this.lineGraphicsOverlay is null)
{
Symbol outline = new SimpleLineSymbol
{
Color = System.Drawing.Color.Cyan,
Style = SimpleLineSymbolStyle.Dash,
Width = 5d,
};
SimpleRenderer renderer = new(outline);
this.lineGraphicsOverlay = new GraphicsOverlay() { Id = LineGraphicsOverlayId, Renderer = renderer, };
this.Engine.MapView.GraphicsOverlays.Add(this.lineGraphicsOverlay);
}
}
public void SetHighlight()
{
try
{
// local flag that checks if any of Line/Poly/Point graphics overlays are null
if (IsHighlightLayerNull)
{
this.InitGraphicsLayers();
}
// Remove all graphics
this.pointGraphicsOverlay.Graphics.Clear();
this.polyGraphicsOverlay.Graphics.Clear();
this.lineGraphicsOverlay.Graphics.Clear();
// Collect all selected Polygon features and return an IEnumerable<Graphic>
var polyGraphics =
this.SelectedFeatures
.Where(static feature => new List<GeometryType>() { GeometryType.Polygon, GeometryType.Envelope }.Contains(feature.Geometry.GeometryType))
.Select(static feature => new Graphic(feature.Geometry));// CreateGraphic(feature.Geometry));
// add our IEnumerable<Graphic> of polygons to the polyGraphicsOverlay
this.polyGraphicsOverlay.Graphics.AddRange(polyGraphics);
var lineGraphics =
this.SelectedFeatures
.Where(static feature => feature.Geometry.GeometryType == GeometryType.Polyline)
.Select(static feature => new Graphic(feature.Geometry));// CreateGraphic(feature.Geometry));
this.lineGraphicsOverlay.Graphics.AddRange(lineGraphics);
var pointGraphics =
this.SelectedFeatures
.Where(static feature => new List<GeometryType>() { GeometryType.Point, GeometryType.Multipoint }.Contains(feature.Geometry.GeometryType))
.Select(static feature => new Graphic(feature.Geometry));// CreateGraphic(feature.Geometry));
this.pointGraphicsOverlay.Graphics.AddRange(pointGraphics);
}
catch (Exception ex)
{
this.LogException(ex);
}
}
// This function currently isn't being used, but I included it for completeness.
// You can see in the `SetHighlight` method that we WERE calling this to create our Graphics
// but we switched it to use a SimpleRenderer instead
private static Graphic CreateGraphic(Geometry geometry)
{
// Create a graphic to display the specified geometry.
Symbol symbol = null;
SimpleLineSymbol outline = new SimpleLineSymbol
{
Color = System.Drawing.Color.Cyan,
Style = SimpleLineSymbolStyle.Dash,
Width = 5d,
};
switch (geometry.GeometryType)
{
// Symbolize with a fill symbol.
case GeometryType.Envelope:
case GeometryType.Polygon:
{
symbol = new SimpleFillSymbol
{
Color = System.Drawing.Color.Cyan,
Style = SimpleFillSymbolStyle.Null,
Outline = outline,
};
break;
}
// Symbolize with a line symbol.
case GeometryType.Polyline:
{
symbol = outline;
break;
}
// Symbolize with a marker symbol.
case GeometryType.Point:
case GeometryType.Multipoint:
{
symbol = new SimpleMarkerSymbol
{
Color = System.Drawing.Color.Cyan,
Style = SimpleMarkerSymbolStyle.X,
Size = 15d,
};
break;
}
}
// Pass back a new graphic with the appropriate symbol.
return new Graphic(geometry, symbol);
}