Creating and updating 1000s of graphics in an ArcGIS Runtime application

3533
0
06-16-2021 03:16 AM
MarkBaird
Esri Regular Contributor
3 0 3,533

In this blog I’ll be sharing techniques for writing efficient applications for rending large numbers of graphics which are being updated very frequently on a map.

The ArcGIS Runtime API for Java is used to demonstrate these techniques, but if you are a developer using another ArcGIS Runtime platform such as .NET, iOS, Qt or Android then the same techniques still apply.

MarkBaird_0-1623831756422.png

Situation awareness style applications display the location of moving assets in a central control room, where a common operational picture needs to be viewed to help with operational decision making. These moving assets can range from tens to thousands in number, depending on what assets are being tracked. I’ve seen this style of application used in control centres for police, ambulance, fire service and military command and control settings for example.

Regardless of the specific use, there is a common theme for how these applications work.  There is usually a feed or stream of data coming from a separate system delivering individual messages which give a status and position updates for items to be displayed on a map display.

MarkBaird_1-1623831889166.png

I will step through an application for an imaginary control centre for despatching and monitoring thousands of emergency vehicles. We will get regular system messages updating vehicle positions and the status of the vehicle which includes states for:

  • Available for new call
  • On route to call
  • Attending call location
  • Off duty

These different vehicle states will be used to show how these vehicle positions are rendered to the map.

Graphics overlay overview

As the data we want to display updates very frequently, it is not suitable to be stored in a data store such as a feature service (which would be backed by a database in the background).  In this case, map points are best displayed as graphics in a graphics overlay, which are stored in-memory and are naturally able to be updated frequently (potentially many times a second), whilst still having a fast-responding application when panning and zooming around to explore the data.

Going back to basics so we all understand where these graphics live, we should all be familiar with the UI control which displays your mapView (2D visualization) or your sceneView (3D visualization).  These controls have a collection where we can store several graphics overlays, each of which can contain multiple graphics.

MarkBaird_2-1623832010930.png

Each graphic is defined by the following items:

  • A geometry (polyline, point or polygon typically)
  • A symbol to define how it is shown on the map (a red dot for example)
  • An optional set of attributes (like fields in a database record)

How to organize your graphics in a graphics overlay

Graphics overlays are incredibly flexible in that you can use them to store all sorts of graphics regardless of their geometry type.  Potentially you could have many thousands of graphics all with different attributes and geometry types all thrown into the same graphics overlay. 

In terms of rendering performance, to a certain extent this is fine, but you will reach a point where some optimisation is needed as you may see a degradation of performance.  If you do see performance issues, I’ve listed some potential improvements you can make:

  • Store geometry types in their own graphics overlays.  For example, if you have some points and polygons, these should be stored in separate graphics overlays.
  • If you have complex geometries (polylines or polygons with lots of vertices), you may find that switching the rendering mode of your graphics overlay from Dynamic to Static will improve performance.  Static rendering doesn’t look as nice when you are zooming in and out, but it sometimes helps especially if you have a low-end graphics card.  The same applies sometimes to point geometries too if you find you are seeing performance issues, but usually these perform fine in Dynamic mode.
  • Renderers would be appropriate to mention here too briefly, since you then go into more detail about them in the next section. Handy having an easy refer to guide here!

Create graphics using a renderer

The simplest way of creating a graphic to display a dot on a map is as follows:

 

// create a red (0xFFFF0000) circle simple marker symbol
SimpleMarkerSymbol redCircleSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.Style.CIRCLE, 0xFFFF0000, 10);

// create graphics and add to graphics overlay
Graphic graphic;
graphic = new Graphic(new Point(-2.72, 56.065, SPATIAL_REFERENCE), redCircleSymbol);
graphicsOverlay.getGraphics().add(graphic);

 

This code has been taken from a sample which you can see in full here.

Note that the graphic contains a new geometry (in this case, a point) and a new symbol.

For a small number of graphics this is fine, but if you are adding lots of graphics to your application which share the same symbol then it is faster and more efficient on your hardware resources to use a renderer of some sort.  Renderers allow you to define the symbol (or symbols) used to display a graphic in a single place so they can be used multiple times.  Depending on your application there are a few different renders to help you do this

Going back to the example where I want to draw a vehicle according to its current operational state, I would choose a Unique value renderer which is associated with an attribute in my graphic.

Workflow for updating graphics

To demonstrate these techniques in action, I have created a sample in a git repository which has a class which generates vehicle update messages.  These messages are generated many times a second for each simulated vehicle and come in the following format:

  • Update Message:
    • VehicleID : Unique identifier for vehicle
    • Position: A point showing the current position
    • Status: Current vehicle status

When receiving these messages which in my app come through at a rate of many thousands a second, we will need a quick way of looking up the graphic representation of a vehicle from that unique vehicle identifier.

Although it is possible to store the vehicle ID as an attribute in a graphic, and stream through each graphic’s attributes to find the vehicle ID, this doesn’t give you access to a fast way of obtaining an individual graphic to updates its position.  I’ve found that using a Java HashMap class to look up the graphic to be nice and fast, as the HashMap is simply a key-value pair structure, but the lookup using the key is very fast and efficient:

private HashMap<String, Graphic> vehicles = new HashMap<>();

Now we have this structure, the processing of messages is simple.  In short when we receive a message , we establish if a graphic exists for the vehicle.  If it exists, we apply a new point geometry to update its location, and if it doesn’t then we just add a new graphic and reference it to the HashMap.  The following code snippet from my sample app shows how simple this is:

/**
 * Method to update a graphic from a vehicle update message.  If the message has come from a new vehicle
 * then a new graphic will be added.
 * @param updateMessage
 */
private void UpdateGraphic(UpdateMessage updateMessage) {
    Point position = updateMessage.getPosition();

    // does graphic already exist?
    if (vehicles.containsKey(updateMessage.getVehicleID())) {
        //update the existing graphic with a new point geometry
        Graphic existingVehicle = vehicles.get(updateMessage.getVehicleID());
        existingVehicle.setGeometry(updateMessage.getPosition());

    } else {
        // create new graphic
        Graphic vehicleGraphic = new Graphic(position);
        vehicleGraphic.getAttributes().put("Status", updateMessage.getStatus().toString());
        graphicsOverlay.getGraphics().add(vehicleGraphic);
        
        // add vehicle graphic to hash map
        vehicles.put(updateMessage.getVehicleID(), vehicleGraphic);
    }
}

In stress testing I’ve managed to easily display 50,000 graphics all of which are being updated at a rate of 20ms.  This was achieved with a standard spec laptop; I’d expect more if you had a fancy gaming spec desktop machine!

If you find this demo application of use for getting your situation awareness style applications of use or have any suggestions on useful additions I can make, please let me know; I’m always keen to hear from our developer customers.