Back to Main Menu

ESRI - Applying custom calculations

Introduction

The assetic_esri python integration by default translates fields from the layer to a corresponding asset field in Assetic.

 

In some cases there is no field in the GIS layer that can be used to set a field in Assetic.  The following are 2 such examples:

  • Apply a prefix to a GIS integer attribute field (unique values) to create an 'Asset Id'.
    • This can be taken a step further to use another GIS attribute field to dictate the prefix to apply.
  • Concatenate 2 or more GIS attribute fields to use as the value in a single field in Assetic

 

To support these various combinations there is an option (introduced in version 1.1.2.0) to define a custom python script file containing the calculation logic to apply to derive field values for use when creating/updating an asset.

Configuration

The XML configuration file, usually saved as arcmap_edit_config.xml is used to configure the calculation.

Calculation script file location

At the top level of the configuration is a node <calculations_file> that is used to define the location of the custom calculation python script.  This file should be saved in a location that is not openly accessible to network users to mimise risk of malicious activity.

In the example below the script file is 'field_calculations.py'

<asseticconfig name = "ESRI">
<calculations_file>C:\users\me\appdata\local\field_calculations.py</calculations_file>
..

Configuration calculation setting

The following xml nodes are used to configure the custom calculation:

XML Node Purpose
calculation The node containing the calculation setting, one node per calculation
 input_fields Contains a collection of XML nodes called 'input'
 input

The ESRI field name used as an input to the calculation.

Each input field is set as the value separate 'input' nodes nested in the 'input_fields' node

 output_field The value for this node is the field that the calculation result is applied to.  This field does not need to exist in the GIS layer.  This field name must be unique and can be referenced as an input field in other sections of the layer configuration.  It cannot be used as an input to another calculation field 
 calculation_tool  The value for this node is the name of the custom python method defined the custom calculation script.

 

The following example defines 2 calculations that set the fields 'asset_id_virtual' and 'asset_name_virtual' which are used for the Assetic 'Asset Id' and 'Asset Name'

 <category>Roads</category>
<corefields>
<asset_id>asset_id_virtual</asset_id>
<asset_name>asset_name_virtual</asset_name>
<asset_class>asset_class</asset_class>
<asset_sub_class>asset_subclass</asset_sub_class>
<id>assetguid</id>
</corefields>
<calculation>
<input_fields>
<field>OBJECTID</field>
</input_fields>
<output_field>asset_id_virtual</output_field>
<calculation_tool>get_road_id</calculation_tool>
</calculation>
<calculation>
<input_fields>
<field>FullStreetName</field>
<field>FromFullStreetName</field>
<field>ToFullStreetName</field>
</input_fields>
<output_field>asset_name_virtual</output_field>
<calculation_tool>get_road_name</calculation_tool>
</calculation>

Calculation Script

This is a custom python script file that applies logic such as the concatenating fields to produce asset name, and adding a prefix to the GIS_ID to create Asset ID.

 

This script must implement the base class CalculationTools.

 

A sample script matching the sample XML is provided below.

 

The method 'get_road_id' applies the prefix 'RD' to the GIS id field and returns the result.

 

The method 'get_road_name' concatenates the 3 input fields and returns the result.

 

The result can be None, or a string or integer or other basic types, but not a list, dictionary, set etc..

"""
field_calculations.py
custom methods that apply calculations for the given inputs
"""
import logging
from assetic_esri import CalculationTools


class FieldCalculations(CalculationTools):
def __init__(self):
super(CalculationTools, self).__init__()
self.logger = logging.getLogger(__name__)

def get_road_id(self, row, input_fields, layer_name):
"""
Return a string that has prefixed the given field
:param row: The data row from the GIS dictionary
:param input_fields: The fields the static method needs
:param layer_name: The name of the layer being processed
"""
if len(input_fields) > 0 and input_fields[0] in row:
return "RD{0}".format(row[input_fields[0]])
else:
raise ValueError("Missing expected dictionary for get_road_id")

def get_road_name(self, row, input_fields, layer_name):
"""
Return a string that has prefixed the given field
:param row: The data row from the GIS dictionary
:param input_fields: The fields the static method needs
:param layer_name: The name of the layer being processed
"""

if "FullStreetName" in row and "FromFullStreetName" in row and \
"ToFullStreetName" in row:
return "{0}-{1} to {2}".format(
row[input_fields[input_fields.index("FullStreetName")]]
, row[input_fields[input_fields.index("FromFullStreetName")]]
, row[input_fields[input_fields.index("ToFullStreetName")]])
else:
raise ValueError("Missing expected dictionary for get_road_name")