Displaying a line as a heat map in arcgis

Viewed 298

I need to represent a route in a map and give a visual representation of a variable. For example, a car wants to go from point A to point B and I want to represent the amount of fuel left in the tank.

I have decided to use ArcGIS to do it but I can't find a proper way to draw a line with changing color. So far what I am doing is creating a new line every two points and changing its color. The problem with this is that in some cases I am drawing more than 4000 lines and it is quite slow to draw.

This is an example of the code that I am using right now (I am using python):

max_elevation = activity_data["Elevation"].max()
min_elevation = activity_data["Elevation"].min()
range_elevation = max_elevation - min_elevation
for index, row in activity_data.iterrows():
    actual_row = activity_data.loc[index]
    next_row = activity_data.loc[index+1]
    if index != activity_data.size:
        sym_poly_aoi = {
        "type": "esriSFS",
        "style": "esriSFSSolid",
        "color": [0,0,0,0],
            "outline": {
            "type": "esriSLS",
            "style": "esriSLSSolid",
            "color": [(actual_row["Elevation"]-min_elevation)/range_elevation*255,0,0,255],
            "width": 3}
        }
        line = {"paths" : [[[actual_row["Longitude"],actual_row["Latitude"]],[next_row["Longitude"],next_row["Latitude"]]]]}
        polyline = Polyline(line)
        map2.draw(polyline,symbol = sym_poly_aoi)

And this is how it looks: enter image description here

The problem is that it takes around 5 min to draw it and I need something faster.

2 Answers

The biggest challenge you have is that the symbology is dependent on a specific attribute (e.g. elevation or fuel_remaining). A single line segment has only one value for an attribute, whereas many small line segments each have a unique value for the same attribute. The full route from A to Z, for example, can have an elevation of 100 -- but the gradient representation of that has A to B (elevation of 25), B to C (elevation of 30), C to D (elevation of 35)...

(There's an ArcGIS Pro Idea out there to symbolize a line with a gradient along its length which may fit your needs, but it's not implemented yet. So that's just an interesting side note, not an answer!)

Rather than try to symbolize a single feature, however, you could focus just on the attributes in your script and then let the symbology rendering happen separately. (Constructing a multi-segment polyline is still going to take some time, but if you're just having it calculate geometry and an attribute instead of also having it render the line segment, it should be faster than your current method.) Once the geometry/attribute is complete, you can just display with graduated color symbology. Make the renderer do the math with built-in functions instead of coding it yourself.

(And side note, I do like the script you have written to solve this problem, and may borrow the concept for a side project I'm working on. But I also completely understand needing things to happen quickly!)

In the end, we decided to use another gis library. In our case we opted to use deck.gl, which also works with python. You just need to structure the data in json format. We use this function to do it with an extra parameter that will be the one we use to change the color:

def line_parameter(data,parameter):

    formated_data = []
    for index in range(len(data.index)-2):
            current_row = data.iloc[index]
            next_row = data.iloc[index+1]

            formated_data.append(
                { parameter: current_row[parameter],
                "start":[current_row["Longitude"],current_row["Latitude"],current_row["Elevation"]],
                "end":[next_row["Longitude"],next_row["Latitude"],next_row["Elevation"]]}
            )
    
    return formated_data

Then we use this function to create the color:

def get_line_colour(data,param):
    max_data = data[param].max()
    min_data = data[param].min()
    multiplier = 255 / (max_data - min_data)
    color = [
        "(HR - " + str(min_data) + ") * " + str(multiplier) ,
        "0",
        "255 - (HR - " + str(min_data) + ") * " + str(multiplier),
        "255",
    ]
    return color

we apply the funcions to our data:

data = pd.read_csv("./some/path")
line_data = get_line_data(data,"HR")
line_colour = get_line_colour(data,"HR")

and finally we use the LineLayer:

line_layer = pdk.Layer(
    "LineLayer",
    line_data,
    get_source_position="start",
    get_target_position="end",
    get_color=line_colour,
    get_width=10,
    highlight_color=[255, 255, 0],
    picking_radius=10,
    auto_highlight=True,
    pickable=True,
)
Related