Hello everyone,
I'm working on a project where I need to draw zigzag lines of a fixed length on a map. The zigzag pattern needs to dynamically adjust based on the zoom level of the map.
Currently, I've implemented this functionality using a PolylineBuilder to construct the zigzag pattern and re-drawing the line every time the zoom level changes, by handling the MapView.ViewpointChanged event.
While this approach works, I’ve encountered significant performance issues when trying to draw and manage several hundred zigzag lines simultaneously. The frequent re-drawing process slows down the application considerably.
I'm looking for suggestions or best practices to improve the efficiency of this process. Specifically:
Is there a way to optimize the re-drawing of multiple polylines based on zoom level?
Are there any advanced techniques or patterns in the Esri ArcGIS Runtime SDK for .NET that could help minimize performance overhead?
Any guidance, examples, or advice would be greatly appreciated!
Here is the code I used to draw and update the zigzag lines:
private void DrawZigZag(string id, double originLatitude, double originLongitude, double rotationAngle)
{
// Get the current map scale for scaling
double mapScale = this.mapMainService.mapView.MapScale;
double scalingFactor = Math.Max(1, mapScale / 500000); // Adjust divisor for sensitivity
// Zigzag appearance configuration
double totalDistanceKm = 150 * 1.852; // Total length (150 nautical miles converted to kilometers)
double zigzagLongitudeDisplacement = 0.01 * scalingFactor; // Horizontal displacement
double segmentDistanceKm = 1.0 * scalingFactor; // Vertical increment
double zigzagLatitudeIncrement = segmentDistanceKm / 111.0; // Convert km to degrees (approximation)
// Convert rotation angle to radians (for CW rotation)
double angleRadians = Math.PI * (-rotationAngle) / 180.0;
double cosAngle = Math.Cos(angleRadians);
double sinAngle = Math.Sin(angleRadians);
// Create a PolylineBuilder
PolylineBuilder pb = new PolylineBuilder(SpatialReferences.Wgs84);
// Starting point (relative coordinates)
double startX = 0.0;
double startY = 0.0;
// Calculate the number of segments
double totalSegments = totalDistanceKm / segmentDistanceKm;
List<MapPoint> points = new List<MapPoint>(); // To store all points for center calculation
// Generate zigzag points
for (int i = 0; i < totalSegments; i++)
{
double x = startX;
double y = startY;
if (i % 2 == 0)
{
x += zigzagLongitudeDisplacement;
}
else
{
x -= zigzagLongitudeDisplacement;
}
y += zigzagLatitudeIncrement;
// Rotate the point
double rotatedX = x * cosAngle - y * sinAngle;
double rotatedY = x * sinAngle + y * cosAngle;
// Convert to geographic coordinates
double rotatedLongitude = originLongitude + rotatedX;
double rotatedLatitude = originLatitude + rotatedY;
// Add the rotated point to the polyline
pb.AddPoint(new MapPoint(rotatedLongitude, rotatedLatitude));
// Update starting coordinates for the next iteration
startX = x;
startY = y;
}
// Create a polyline
Polyline polyline = pb.ToGeometry();
// Create a symbol for the polyline
var lineSymbol = new SimpleLineSymbol(SimpleLineSymbolStyle.Solid, System.Drawing.Color.Red, 2);
// Create attributes for label
List<KeyValuePair<string, object?>> attributes = new List<KeyValuePair<string, object?>>()
{
new KeyValuePair<string, object?>("name", id.ToString()[..10]),
new KeyValuePair<string, object?>("name", id.ToString()[..10])
};
// Create a graphic and add it to the overlay
Graphic polylineGraphic = new Graphic(polyline, attributes, lineSymbol)
{
};
this.GraphicsOverlay.Graphics.Add(polylineGraphic);
}
public void UpdateZigZags()
{
// Clear all graphics to redraw
this.GraphicsOverlay.Graphics.Clear();
// Redraw each zigzag with the updated scale
foreach (var kvp in this.zigZagLines)
{
var id = kvp.Key;
var (latitude, longitude, rotation) = kvp.Value;
DrawZigZag(id, latitude, longitude, rotation);
}
}
Thank you in advance!
Solved! Go to Solution.
You can use CIM symbols to do this instead. They have this feature "built in".
Here's an example of a JSON version of a zigzag line symbol:
{"type":"CIMLineSymbol","symbolLayers":[
{"type":"CIMSolidStroke","effects": [
{"type":"CIMGeometricEffectWave","amplitude":10,"period":3,"seed":1,"waveform":"Triangle"}]
,"enable":true,"capStyle":"Butt","joinStyle":"Round","lineStyle3D":"Strip",
"miterLimit":10,"width":1,"height3D":1,"anchor3D":"Center","color":[104,104,104,255]}]}
You can play with the parameters to get what you want.
Example of creating a symbol from this JSON:
Symbol s = Symbol.FromJson("{\"type\":\"CIMLineSymbol\",\"symbolLayers\":[{\"type\":\"CIMSolidStroke\",\"effects\":[{\"type\":\"CIMGeometricEffectWave\",\"amplitude\":10,\"period\":3,\"seed\":1,\"waveform\":\"Triangle\"}],\"enable\":true,\"capStyle\":\"Butt\",\"joinStyle\":\"Round\",\"lineStyle3D\":\"Strip\",\"miterLimit\":10,\"width\":1,\"height3D\":1,\"anchor3D\":\"Center\",\"color\":[104,104,104,255]}]}")!;
SimpleRenderer r = new SimpleRenderer(s);
layer.Renderer = r;
You can also use the CIM APIs to build it up, but JSON is just a quick way to show the concept.
I think you should look into CIM symbols. I don't work with runtime, but I think it's supported. You can use CIM to build multi-layered symbols that fit your custom needs. There are many examples for CIM symbols for js here: Esri Web Style Symbols (2D) | Overview | ArcGIS Maps SDK for JavaScript 4.32 | Esri Developer
Search for "Zigzag". You can customize the amplitude and density of the waves. Your geometries will be straight, but they will be displayed as zig-zags. It will also resize based on zoom level and should render a lot faster.
What is the profiler showing as your hot spots? If the lion's share of the time is spent in rendering-related code you might be out of luck if you want an immediate render using world-space data. Building a full list of graphics elements before adding them to the overlay might help here; you can even try building them in a thread or async task that you cancel on each event to avoid blasting graphics at the map when the user makes quick adjustments.
If the bottleneck is constructing the zigzags themselves, two things you can try are:
You can use CIM symbols to do this instead. They have this feature "built in".
Here's an example of a JSON version of a zigzag line symbol:
{"type":"CIMLineSymbol","symbolLayers":[
{"type":"CIMSolidStroke","effects": [
{"type":"CIMGeometricEffectWave","amplitude":10,"period":3,"seed":1,"waveform":"Triangle"}]
,"enable":true,"capStyle":"Butt","joinStyle":"Round","lineStyle3D":"Strip",
"miterLimit":10,"width":1,"height3D":1,"anchor3D":"Center","color":[104,104,104,255]}]}
You can play with the parameters to get what you want.
Example of creating a symbol from this JSON:
Symbol s = Symbol.FromJson("{\"type\":\"CIMLineSymbol\",\"symbolLayers\":[{\"type\":\"CIMSolidStroke\",\"effects\":[{\"type\":\"CIMGeometricEffectWave\",\"amplitude\":10,\"period\":3,\"seed\":1,\"waveform\":\"Triangle\"}],\"enable\":true,\"capStyle\":\"Butt\",\"joinStyle\":\"Round\",\"lineStyle3D\":\"Strip\",\"miterLimit\":10,\"width\":1,\"height3D\":1,\"anchor3D\":\"Center\",\"color\":[104,104,104,255]}]}")!;
SimpleRenderer r = new SimpleRenderer(s);
layer.Renderer = r;
You can also use the CIM APIs to build it up, but JSON is just a quick way to show the concept.
Hi everyone,
Thanks so much for your suggestions, especially about CIM symbols. I implemented them, and the performance has improved significantly—it works great now!
I used the zigzag CIM symbol from this resource: Esri Web Style Symbols (2D) | Overview | ArcGIS Maps SDK for JavaScript 4.32 | Esri Developer
However, I’ve noticed an issue with the appearance of the zigzag line—it seems to be cut off in the middle. I'll attach a photo (see below). Has anyone experienced this or know how to fix it?
Cut off Zigzag
Here is how the normal line should look like for reference:
Normal Zigzag
Thanks again for your help!
Try checking the geometry. Is it one long line, or maybe a lot of small ones? Does the broken zigzag stay all the time, or just on specific zoom levels?
If geometry is fine, try changing the CIMGeometricEffectWave parameters: amplitude, period and seed
Try changing cap style (Butt, Round, Square), or join style (Bevel, Round, Miter)
If none of that works, this is probably a bug.