Select to view content in your preferred language

Efficiently Drawing a Zigzag Line

324
5
Jump to solution
03-13-2025 08:43 AM
Labels (3)
AaronBarkan
New Contributor

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!

0 Kudos
1 Solution

Accepted Solutions
dotMorten_esri
Esri Notable Contributor

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.

View solution in original post

5 Replies
Edvinas_S
Esri Contributor

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.

DavidSolari
MVP Regular Contributor

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:

  1. Hoist as many reusable heap-allocated objects out of the function as possible, the less garbage your zigzags accumulate the better.
  2. Do all calculations in the map's current coordinate system and try to use the GeometryEngine extension methods to do as much of the math as possible to avoid coordinate errors. This eliminates any reprojection between your graphics and the map.
dotMorten_esri
Esri Notable Contributor

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.

AaronBarkan
New Contributor

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 ZigzagCut off Zigzag

Here is how the normal line should look like for reference:

Normal ZigzagNormal Zigzag

Thanks again for your help!

0 Kudos
Edvinas_S
Esri Contributor

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.