Programmer’s Guide

Obtaining Help

Via STK Programming documentation

The STK Python API help documents the method/property definitions with syntax using the Python typing module. The following example method and property from the IAgStkObjectRoot interface show this syntax.

def GetObjectFromPath(self, ObjectPath:str) -> "IAgStkObject":

@property
def CurrentScenario(self) -> "IAgStkObject":

These examples show that interface methods and properties are methods called on an object ("self"). Input argument names (e.g., "ObjectPath") are shown for methods along with the type of the argument (in this example, Python str type). The return type is shown after the "->" operator and STK Object Model types (e.g., "IAgStkObject") are shown in quotes to help IDEs resolve the type names. The Python API programmer may call these methods and properties on an IAgStkObjectRoot object in the expected way. In the following example, an IAgStkObjectRoot object named "root" has already been created.

my_object = root.GetObjectFromPath('My Object Path')

my_scenario = root.CurrentScenario

Within interactive Python

You can use the Python help function to display the documentation of modules, functions, classes, keywords etc. Below is an example printing the help for the class returned by the object model root current scenario property.

# Start new instance of STK Engine using the new API
from agi.stk12.stkengine import STKEngine
from agi.stk12.stkobjects import *
stk = STKEngine.StartApplication(noGraphics=False) # optionally, noGraphics = True

# Get the IAgStkObjectRoot interface
root = stk.NewObjectRoot()

# Create a new scenario
root.NewScenario('HelpDemo')

# Output the documentation for the class returned by the CurrentScenario property
print(help(root.CurrentScenario))

This produces the following output:

Help on AgScenario in module agi.stk12.stkobjects object:

class AgScenario(IAgScenario, IAgStkObject, IAgLifetimeInformation)
| Class defining the Scenario object.
|
| Method resolution order:
| AgScenario
| IAgScenario
| IAgStkObject
| IAgLifetimeInformation
| builtins.object
|
| Methods defined here:
|
| __init__(self, sourceObject=None)
| Initialize self. See help(type(self)) for accurate signature.
|
| ----------------------------------------------------------------------
| Methods inherited from IAgScenario:
|
| GetAccessBetweenObjectsByPath(self, objectPath1:str, objectPath2:str) -> 'IAgStkAccess'
| Returns the access object currently associated with the two STK objects specified using their paths. The paths can be fully-qualified or truncated.
|
| GetExistingAccesses(self) -> list
| Returns an array of existing accesses in the current scenario. The result is a two-dimensional array of triplets where each triplet contains the paths of two objects participating in the access and a flag indicating whether the access is computed.
|
| SetDirty(self) -> None
| Sets the flag indicating the scenario has been modified.
|
| SetTimePeriod(self, startTime:Any, stopTime:Any) -> None
-- More --

Module Mapping

The following table provides the corresponding library for each Python module.

library Module

STK Graphics Primitives

agi.stk12.graphics

STK Access Constraint Plugin

agi.stk12.plugins.accessconstraintplugin

STK Attribute Automation

agi.stk12.plugins.attrautomation

STK Communications and Radar foundation

agi.stk12.plugins.commrdrfoundation

STK Coordinate(VGT) plugins

agi.stk12.plugins.crdnplugin

STK Astrogator plugin

agi.stk12.plugins.gatorplugin

STK HPOP plugins

agi.stk12.plugins.hpopplugin

STK Propagator plugins

agi.stk12.plugins.propagator

STK plugin

agi.stk12.plugins.stkplugin

STK Radar plugins

agi.stk12.plugins.stkradarplugin

STK Utilities plugin

agi.stk12.plugins.utplugin

STK Astro plugins

agi.stk12.plugins.asplugin

STK Search plugin

agi.stk12.plugins.searchplugin

STK Objects

agi.stk12.stkobjects

STK Aviator

agi.stk12.stkobjects.aviator

STK Aviator MATLAB

agi.stk12.stkobjects.aviator.matlab

STK Util

agi.stk12.stkutil

STK X

agi.stk12.stkx

STK UI Application

agi.stk12.uiapplication

STK UI Core

agi.stk12.uicore

STK Vector Geometry Tool

agi.stk12.vgt

STK Desktop

This section describes how to use the API with STK Desktop. The STKDesktop.StartApplication and STKDesktop.AttachToApplication methods are available to obtain the STKDesktopApplication class and begin interacting with STK through the the AgUiApplication API. From the application interface, the most common way to begin working with STK is to use the IAgStkObjectRoot interface, which is accessible as the Root property of the STKDesktopApplication object.

Starting a new STK Desktop instance

Use the STKDesktop.StartApplication method to start a new STK application session. The StartApplication method has optional arguments as a convenience to set commonly used properties:

ArgumentDescription
set visible=TrueStart STK in visible mode
set userControl=TrueKeeps STK open after finishing a Python script
grpc_server (bool, default=False)Specify True to start STK on the current machine with the gRPC server active and connect to it using gRPC
grpc_host (string, default = "0.0.0.0")A valid DNS host name for the STK gRPC server
grpc_port (init, default=40704)A valid TCP port for the STK gRPC server

[Python - STK API]
# Start new instance of STK using the new API
from agi.stk12.stkdesktop import STKDesktop
from agi.stk12.stkobjects import *
stk = STKDesktop.StartApplication(visible=True) #using optional visible argument

# Get the IAgStkObjectRoot interface
root = stk.Root

Attaching to a running instance of STK Desktop

Use the STKDesktop.AttachToApplication method to attach to a running STK desktop application. The AttachToApplication method has additional arguments to specify the Process ID (pid) if more than one STK application is running:

Argument Description
pid (int, default = None) The process ID for the STK Desktop instance to attach to. Not applicable if gRPC is used.
grpc_server (bool, default = False) Specify True to attach to a running STK gRPC server.
grpc_host (string, default = "localhost") A valid DNS host name for the STK gRPC server. "localhost" connects to the local server.
grpc_port (init, default = 40704) A valid TCP port for the STK gRPC server.
[Python - STK API]
# Get reference to running STK instance using the new API
from agi.stk12.stkdesktop import STKDesktop
from agi.stk12.stkobjects import *
stk = STKDesktop.AttachToApplication()

# Get the IAgStkObjectRoot interface
root = stk.Root

Finishing your work with STK Desktop

STKDesktopApplication provides a ShutDown method that is the recommended way to terminate the connection to STK and free up resources. Set the UserControl property on STKDesktopApplication or when calling StartApplication to set the application behavior after the call to ShutDown.

[Python - STK API]
# Get reference to running STK instance using the new API
from agi.stk12.stkdesktop import STKDesktop
from agi.stk12.stkobjects import *
stk = STKDesktop.StartApplication(userControl=False)

# Do work...
stk.ShutDown()

Advanced topic: marshalling across threads

STKDesktop.CreateThreadMarshaller (stk_object_to_marshal) was added in STK 12.2.0 to assist with marshalling STK objects between Python threads. Because the STK Object model is not inherently thread-safe, this helper is needed to safely execute commands from two or more threads.

As this is an advanced feature, and only experienced users should implement it, and then only when necessary.

When using this feature, you are responsible for properly synchronizing across threads. An example application of this (shown below) would be to marshal IAgStkObjectRoot to a new thread and call NewScenario, so that the Python application main thread is not blocked while the scenario is created.

STKDesktop.CreateThreadMarshaller returns agi.stk12.stkdesktop.ThreadMarshaller, which has the methods InitializeThread, ReleaseThread, and GetMarshalledToCurrentThread. InitializeThread must be called on the destination thread before the marshalled object can be used.

GetMarshalledToCurrentThread is called to return a copy of the marshalled object that may be used on the current thread. ReleaseThread must be called before the destination thread exits.

Each ThreadMarshaller may be used only once; the same ThreadMarshaller may not be passed to two or more threads.

[Python - STK API]
import threading
from agi.stk12.stkdesktop import STKDesktop

def asynchronous_new_scenario(rootMarshaller):
rootMarshaller.InitializeThread()
root = rootMarshaller.GetMarshalledToCurrentThread()
print(f'Creating a new scenario from thread {threading.get_ident()}.')
root.NewScenario('MyScenario')
rootMarshaller.ReleaseThread()

if __name__=='__main__':
print(f'Starting STK from thread {threading.get_ident()}.')
stk = STKDesktop.StartApplication(visible=True, userControl=False)
root = stk.Root
rootMarshaller = STKDesktop.CreateThreadMarshaller(root)
t = threading.Thread(target=asynchronous_new_scenario, args=(rootMarshaller,), daemon=True)
t.start()
t.join()
print(f'Terminating example from thread {threading.get_ident()}.')
stk.ShutDown()

STKRuntime

STKRuntime is an executable that serves STK Engine functionality vai gRPC. Use agi.stk12.stkruntime to start or attach to a running STKRuntime application. Once the STKRuntimeAPplication object is obtained, interact with STK, via IAgStkObjectRoot obtained from calling STKRuntimeApplication.NewObjectRoot(). Shutting down the remote STKRuntime process is possible by calling STKRuntimeApplication.ShutDown(), or using the userControl=False option when starting the application.

Starting a new STKRuntime instance

STKRuntime may be started on the local machine using STKRuntime.StartApplication(). While STKRuntime offers STK Engine functionality similar to the STKEngine module, there are a few key differences.

  1. STKEngine runs STK in-process with Python, whereas STKRuntime is out-of-process using gRPC to communicate.
  2. STKRuntime does not offer visualizations at this time.
Options Description
grpc_host (string, default = "0.0.0.0") A valid DNS host name for the STKRuntime gRPC server.
grpc_port (int, default = 40704) A valid TCP port for the STKRuntime gRPC server.
userControl (bool, default = False) Specify True to leave the STKRuntime application running (in the user's control) after Python exits.
noGraphics (bool, default = True) Disables graphics calculations within the STKRuntime application to improve performance and remove dependencies on graphics libraries.
[STKRuntime Python - API]
from agi.stk12.stkruntime import STKRuntime
stk = STKRuntime.StartApplication(grpc_host="0.0.0.0", grpc_port=40704, userControl=False)
print(stk.Version)
root=stk.NewObjectRoot()
root.NewScenario("MyScenario")

Attaching to a running STKRuntime instance

To attach to a running STKRuntime application via gRPC, you can use STKRuntime.AttachToApplication(). To shut down the STK Runtime application, STKRuntimeApplication.ShutDown() must be called.

Option Description
grpc_host (string, default = "localhost") A valid DNS host name for the STKRuntime gRPC server.
grpc_port (int, default = 40704) A valid TCP port for the STKRuntime gRPC server.
[STKRuntime Python - API]
from agi.stk12.stkruntime import STKRuntime
stk = STKRuntime.AttachToApplication(grpc_host="localhost", grpc_port=40704)
print(stk.Version)
root=stk.NewObjectRoot()
stk.ShutDown()

STK Engine

This section describes how to use the API with STK Engine. The STK Engine API is supported on both Windows and Linux, although AGI has not implemented some features, such as events. STK Engine runs in-process in your Python script, so unlike STK Desktop, only one instance of engine is possible, which is started using STKEngine.StartApplication, returning the STKEngineApplication class and giving access to the AgSTKXApplication API. Unlike STKDesktopApplication, the object model root is not a property and a new root object may be obtained from the NewObjectRoot method on the STKEngineApplication object.

Starting STK Engine

[Python - STK API]
# Start new instance of STK Engine using the new API
from agi.stk12.stkengine import STKEngine
from agi.stk12.stkobjects import *
stk = STKEngine.StartApplication(noGraphics=False) # optionally, noGraphics = True

# Get the IAgStkObjectRoot interface
root = stk.NewObjectRoot()

Finishing your work with STK Engine

STKEngineApplication provides a ShutDown method that is the recommended way to terminate the connection to STK and free up resources. After calling ShutDown, it is no longer valid to start a new engine application in the current process.

[Python - STK API]
# Get reference to running STK instance using the new API
from agi.stk12.stkengine import STKEngine
from agi.stk12.stkobjects import *
stk = STKEngine.StartApplication()

# Do work...
stk.ShutDown()

Advanced topic: STK Engine timer loop

Timer loops are an advanced topic that most API users can ignore. Some features in STK rely on a timer loop to function properly, for example updating graphics windows in a globe control, flushing the log file messages, or establishing a Connect socket to STK Engine. There are different ways of establishing a timer loop in a Python main thread, and these ways have different trade-offs that may affect your application.

On Windows, the only way to activate a timer loop is to use the loops implicitly available in the interactive Python interpreter or in the Tkinter mainloop. One of these will need to be running to establish a Connect socket, for example.

On Linux, when graphics are active, it is assumed that globe or map controls will be used, and so the timer loop defaults to using the Tkinter mainloop, which will be running when using the tkinter-based controls. In no-graphics mode, signal-based timer loops are available. By default, STKEngine will use a timer loop that uses the SIGALRM signal. If this creates a conflict with your application, there is also an option to specify a SIGRT signal number to use. Contact AGI support to learn more.

If directed by AGI Tech Support, you may override timer operations on Linux using the environment variable STK_PYTHONAPI_TIMERTYPE, by setting it to a numeric value relating to the enum agi.stk12.stkengine.STKEngineTimerType. The default value is SigAlarm (4) in no-graphics mode and TkinterMainloop (2) in graphics mode. You may use InteractivePython (3) in the interactive interpreter, and it will not use any signals nor require the tkinter mainloop. Use SigRt (5) to specify a different signal than SIGALRM. The default is SIGRTMIN, and you may change this to SIGRTMIN+X, where X is specified using a second environment variable STK_PYTHONAPI_TIMERTYPE5_SIGRTMIN_OFFSET=X. If SIGRTMIN+X > SIGRTMAX, you will get an exception.

Tkinter Globe, Map and Gfx Analysis Controls

This section shows how to use the API with the Tkinter GlobeControl, MapControl, and GfxAnalysisControl classes. Refer to the STK X controls topic for a description of the controls. Refer to the Custom Application Samples table for a list of Python code samples demonstrating the use of the STK Python controls.

Create a Tkinter window with a globe control

[Python - STK API]
from tkinter import Tk, BOTH, LEFT
from agi.stk12.stkengine import *
from agi.stk12.stkengine.tkcontrols import GlobeControl, MapControl

stk = STKEngine.StartApplication(noGraphics=False)
root = stk.NewObjectRoot()
root.NewScenario("Example_Scenario")

window = Tk()

# Create the globe control and add it to the Tkinter window
globeControl = GlobeControl(window, width=640, height=400)
globeControl.pack(fill=BOTH, side=LEFT)

# Create the map control and add it to the Tkinter window
mapControl = MapControl(window, width=640, height=400)
mapControl.pack(fill=BOTH, side=LEFT)

window.mainloop()

root.CloseScenario()
stk.ShutDown()

Data Types

This section describes the more complex data types used with the STK Python API beyond the basic Python data types such as float, int, str, and bool.

Type hints

Most argument and return types are specified using type hints with Python's typing library. In the case that more than one type is possible (such as an argument that may be a string or a float), typing.Any is used as the type hint. In those situations, consulting the documentation for that method is advised. Type hints that are STK interfaces may represent objects that are subclasses of that interface.

Enumerations

Enumeration classes are located in the STK Object Model modules (e.g. agi.stk12.stkobjects). Most inherit from Python's enum.IntEnum class while a few inherit from enum.IntFlag and may be combined using the | operator to select multiple options from within the enumeration.

[Python - STK API]
from agi.stk12.stkobjects import AgESTKObjectType
fac = ObjectRoot.CurrentScenario.Children.New(AgESTKObjectType.eFacility, "fac1")

from agi.stk12.graphics import AgEStkGraphicsCylinderFill

#AgEStkGraphicsCylinderFill inherits from IntFlag and may be combined
cyl_fill = AgEStkGraphicsCylinderFill.eStkGraphicsCylinderFillBottomCap | AgEStkGraphicsCylinderFill.eStkGraphicsCylinderFillTopCap

Arrays

Many methods in the STK API take as input or return arrays. In the Python API, array values are represented using the list class.

[Python - STK API]
from agi.stk12.stkdesktop import STKDesktop
from agi.stk12.stkutil import AgEExecMultiCmdResultAction
stk = STKDesktop.StartApplication()

connect_commands = ['GetStkVersion /', 'New / Scenario ExampleScenario'] #use a list of strings
command_results = stk.ExecuteMultipleCommands(commands, AgEExecMultiCmdResultAction.eContinueOnError)

STK interfaces and classes

The STK object model is comprised of programming interfaces that are implemented by Python classes located in the provided modules. With few exceptions, classes returned from API methods begin with "Ag" and will inherit from one or more interfaces (beginning with "IAg"). You may immediately access any method from the inherited interfaces without casting, although in some situations casting may help with your IDE auto-complete feature (see Refactoring comtypes QueryInterface calls for more information). These classes have a reference to an STK object; this reference will be removed upon calling del() on the Python class. Because these classes are references to STK objects, creating them directly from Python will not be successful; objects must be returned from STK API methods.

[Python - STK API]
from agi.stk12.stkobjects import AgFacility, AgESTKObjectType

try:
# this facility is not a valid STK reference
my_facility_attempt = AgFacility()
my_facility_attempt.HeightAboveGround = 123.4
except STKRuntimeError as e:
print(e)

# this facility represents a valid STK object
my_facility = AgFacility(ObjectRoot.CurrentScenario.Children.New(AgESTKObjectType.eFacility, "fac1"))
my_facility.HeightAboveGround = 123.4

Collections

Many of the interfaces in the STK API represent collections of items; such interfaces have the word "Collection" as part of their name. These classes have an "Item()" method that may be used to get an indexed item from the collection, but they also support Python indexing and iteration.

[Python - STK API]
from agi.stk12.stkdesktop import STKDesktop
from agi.stk12.stkutil import AgEExecMultiCmdResultAction
stk = STKDesktop.StartApplication()

connect_commands = ['GetStkVersion /', 'New / Scenario ExampleScenario']
command_results = stk.Root.ExecuteMultipleCommands(connect_commands, AgEExecMultiCmdResultAction.eContinueOnError)

first_message = command_results.Item(0)
also_first_message = command_results[0]
for message in command_results:
print(message.Count)

Multiple return values

Some methods in the API return multiple values rather than returning one list. The multiple values are returned as a tuple.

[Python - STK API]
(x, y, z) = my_facility.Position.QueryCartesian()

Colors

The agi.stk12.utilities.colors module contains the Color, ColorRGBA, and Colors classes used by the STK Python API. The Color class represents an opaque color constructed from RGB values in the range [0, 255]. ColorRGBA represents a variably-translucent, 4-channel color constructed from RGBA values in the range [0, 255]. ColorRGBA may not be used in methods expecting a 3-channel color. Colors contains an assortment of named colors as well as factory methods to create Color or ColorRGBA objects from RGB(A) values.

[Python - STK API]
from agi.stk12.utilities.colors import Color, Colors
fac = ObjectRoot.CurrentScenario.Children.New(AgESTKObjectType.eFacility, "fac1")

fac.Graphics.Color = Colors.Blue
fac.Graphics.Color = Color.FromRGB(127, 255, 212)
(r, g, b) = fac.Graphics.Color.GetRGB()

Certain methods require a list of 4-channel RGBA color values for defining per-vertex colors on a geometry. Such a list should be constructed in Python as a list of Color and/or ColorRGBA objects. Color objects always have alpha=255 (fully opaque), whereas alpha may be specified when using the ColorRGBA class. An example of these usages is provided below.

[Python - STK API]
from agi.stk12.stkdesktop import STKDesktop
from agi.stk12.utilities.colors import Color, Colors, ColorRGBA

stk = STKDesktop.StartApplication(visible=True, userControl = False)
root = stk.Root
root.NewScenario('test')
manager = root.CurrentScenario.SceneManager
point = manager.Initializers.PointBatchPrimitive.Initialize()
lla_pts = [ 39.88, -75.25, 0,
38.85, -77.04, 0,
37.37, -121.92, 0]
colors = [ Colors.Red,
ColorRGBA(Colors.Blue, 127),
Colors.FromRGBA(0, 255, 0, 127)]
point.SetCartographicWithColors('Earth', lla_pts, colors)

Data Types for Data Analysis

This section describes data types you can use in the STK Python API for data analysis. These data types are standard data types used in popular Python data science and scientific computing libraries. These data types are available starting in the STK 12.6+.

NumPy arrays

You can convert a dataset collection in row format to a NumPy array. NumPy arrays are N-dimensional arrays with support for fast vector operations, indexing, and broadcasting. NumPy also offers a broad collection of standard mathematical functions used in scientific computing. The NumPy array is a widely supported data structure, in popular Python scientific computing and machine learning libraries.

The ToNumpyArray() method, called on IAgDrDataSetCollection, returns a data provider’s results dataset collection as a 2D NumPy array. This array has a shape equal to the total number of rows in the dataset collection and the total number of unique columns fields in the dataset collection. For example, if the computed All Region By Pass data provider results dataset collection contains 100 rows and 11 column fields, the ToNumpyArray() method would return a NumPy array of the entire result set and would have a shape of (100, 11), where 100 is the number of rows and 11 is the number of columns.

To use this functionality, you must have NumPy installed in your local Python development environment.

Here is an example of using NumPy arrays for flight profile data.

[Python - STK API]
from scipy.spatial import ConvexHull
import matplotlib.pyplot as plt

# compute data provider results for an aircraft's Flight Profile By Time
field_names = ['Mach #', 'Altitude']
time_step_sec = 1.0

flight_profile_data_provider = aircraft.DataProviders.Item('Flight Profile By Time')
flight_profile_data = flight_profile_data_provider.ExecElements(scenario.StartTime, scenario.StopTime, time_step_sec, field_names)

# convert dataset collection in a row format as a Numpy array
flight_profile_data_arr = flight_profile_data.DataSets.ToNumpyArray()

# get shape of array (number of rows, number of columns)
print(flight_profile_data_arr.shape)

# plot estimated fligth envelope as a convex hull
hull = ConvexHull(flight_profile_data_arr)

plt.figure(figsize=(15,10))
for simplex in hull.simplices:
plt.plot(flight_profile_data_arr[simplex, 1], flight_profile_data_arr[simplex, 0], color="darkblue")

plt.title('Estimated Flight Envelope', fontsize=15)
plt.xlabel('Mach Number', fontsize=15)
plt.ylabel('Altitude', fontsize=15)

plt.tick_params(axis='x', labelsize=15)
plt.tick_params(axis='y', labelsize=15)
plt.grid()

The resulting flight data plot then looks like this:

For more information, see NumPy.

Pandas DataFrame

You can convert a dataset collection in row format as a Pandas DataFrame. DataFrames are the key to Pandas’ fast and efficient data manipulation and analysis functionality. They are a two-dimensional, tabular data structure with labeled indexing for rows and columns, where the columns can contain data of various data types. DataFrames supports powerful aggregation and transformation functionality, time series functionality, merging and joining operations of datasets, hierarchical indexing, vectorized operations, flexible reshaping functionality, and much more.

The ToPandasDataFrame() method called on IAgDrDataSetCollection, returns a data provider’s results dataset collection as Pandas DataFrame. The DataFrame row index length, equal to the total number of rows in the dataset collection and each column in the DataFrame, maps to a unique field name in the dataset collection. For example, if the computed Flight Profile by Time data provider results dataset collection contains 6000 rows and 100 fields column fields, the returned DataFrame will have a row index length of 6000 and 100 columns.

To use this functionality, you must have Pandas installed in your local Python development environment.

Here are four examples of using Pandas DataFrame.

Example 1: Convert All Regions By Pass data provider results to a Pandas DataFrame with a default numeric row index. The Python implementation would look like this:

[Python - STK API]
# compute data provider results for All Regions by Pass coverage
coverage_data_provider = coverage.DataProviders.Item('All Regions By Pass')
coverage_data = coverage_data_provider.Exec()

# convert dataset collection in a row format as a Pandas DataFrame with default numeric row index
coverage_arr = coverage_data.DataSets.ToPandasDataFrame()

The ToPandasDataFrame() method supports setting a single column as the index. To create a hierarchical index or a composite index comprised of more than a single column, get your data provider’s results dataset collection as a Pandas DataFrame with the default numeric index, then update the index accordingly.

[Python - STK API]
# compute data provider results for basic Access
field_names = ['Access Number', 'Start Time', 'Stop Time', 'Duration']
time_step_sec = 1.0

access_data_provider = facility_sensor_satellite_access.DataProviders.Item('Access')
access_data = access_data_provider.ExecElements(scenario.StartTime, scenario.StopTime, time_step_sec, field_names)

# convert dataset collection in a row format as a Pandas DataFrame
index_column = 'Access Number'
access_data_df = access_data.DataSets.ToPandasDataFrame(index_element_name=index_column)

Example 2: Compute descriptive statistics access measurements. The implementation in Python would be:

[Python - STK API]
# compute data provider results for All Regions by Pass coverage
coverage_data_provider = coverage.DataProviders.Item('All Regions By Pass')
coverage_data = coverage_data_provider.Exec()

# convert dataset collection in a row format as a Pandas DataFrame with default numeric row index
all_regions_coverage_df = coverage_data.DataSets.ToPandasDataFrame()

# comptue descriptive statistics of Duration, Percent Coverage, Area Coverage
all_regions_coverage_df[['duration', 'percent coverage', 'area coverage']].describe()

This produces the following data table:

Example 3: Compute descriptive statistics access measurements grouped by Asset Name (Satellite Names). The Python implementation would be as follows:

[Python - STK API]
def q1(x):
return x.quantile(0.25)

def q2(x):
return x.quantile(0.50)

def q3(x):
return x.quantile(0.75)

# compute data provider results for All Regions by Pass coverage
coverage_data_provider = coverage.DataProviders.Item('All Regions By Pass')
coverage_data = coverage_data_provider.Exec()

# convert dataset collection in a row format as a Pandas DataFrame with default numeric row index
all_regions_coverage_df = coverage_data.DataSets.ToPandasDataFrame()

# comptue descriptive statistics of Duration, Percent Coverage, Area Coverage grouped by Asset Name (Satellite)
all_region_coverage_df.groupby('asset name').agg({'duration': ['mean', 'min', q1, q2, q3, 'max'],
'percent coverage': ['mean', 'min', q1, q2, q3, 'max'],
'area coverage': ['mean', 'min', q1, q2, q3, 'max']
}).T

This produces the following data table:

Example 4: Plot a heat map of Duration By Asset (Satellite) for each access region. The Python implementation would be:

[Python - STK API]
import seaborn as sns; sns.set_style('ticks')
from matplotlib import pyplot as plt

# compute data provider results for All Regions by Pass coverage
coverage_data_provider = coverage.DataProviders.Item('All Regions By Pass')
coverage_data = coverage_data_provider.Exec()

# convert dataset collection in a row format as a Pandas DataFrame with default numeric row index
coverage_all_regions_elements = coverage_data_provider.Elements
all_regions_coverage_df = coverage_data.DataSets.ToPandasDataFrame(dataProviderElements=coverage_all_regions_elements)

# reshape the DataFrame based on column values
pivot = all_region_coverage_df.pivot_table(index='region name', columns='asset name', values='duration')

# plot heat map that shows duration by asset name by region
plt.figure(figsize=(20,10))
ax = sns.heatmap(pivot, cmap="YlGnBu")

ax.set_xlabel('Duration by Asset', fontsize=20)
ax.set_ylabel('Region Name', fontsize=20)
plt.tick_params(axis='x', labelsize=15)
plt.tick_params(axis='y', labelsize=15)

This produces the following data map:

See Pandas for more information, including library documentation.

Exceptions

The table below describes the exceptions that are provided by the agi.stk12.utilities.exceptions module with the STK Python API.

Exception Description

STKInitializationError

Raised in STKDesktop and STKEngine when unable to initialize or attach to STK.

STKInvalidCastError

Raised when attempting to cast an object to an unsupported interface or class type.

STKRuntimeError

Raised when an STK method call fails.

STKAttributeError

Raised when attempting to set an unrecognized attribute within the STK API.

Make sure the spelling and capitalization is correct.

Events

Support for events was added in STK 12.2.0. Events can be accessed directly in applicable parent objects, as seen in the table below.

Event Interface Parent Object

IAgStkObjectRootEvents

IAgStkObjectRoot

IAgSTKXApplicationEvents

agi.stk12.stkengine.STKEngineApplication

IAgUiAxVOCntrlEvents

agi.stk12.stkengine.tkcontrols.GlobeControl

IAgUiAx2DCntrlEvents

agi.stk12.stkengine.tkcontrols.MapControl

IAgStkGraphicsSceneEvents

IAgStkGraphicsScene

IAgStkGraphicsKmlGraphicsEvents

IAgStkGraphicsKmlGraphics

IAgStkGraphicsImageCollectionEvents

IAgStkGraphicsImageCollection

IAgStkGraphicsTerrainCollectionEvents

IAgStkGraphicsTerrainCollection

Events are accessed through the Subscribe() method on the parent object, which returns an event handler subscribed to events on the queried object. You can add or remove Event callbacks in the event handler using the "+=" and "-=" operators; these operators will change the callbacks that will get executed by the event but will not affect whether the handler remains subscribed. The event handler should be unsubscribed using the Unsubscribe() method when event handling is no longer needed. Refer to the following example for using IAgStkObjectRootEvents.

[Python - STK API]
from agi.stk12.stkengine import STKEngine

def onScenarioNewCallback(Path:str):
print(f'Scenario {Path} has been created.')

stk = STKEngine.StartApplication()
root = stk.NewObjectRoot()
stkObjectRootEvents = root.Subscribe()
stkObjectRootEvents.OnScenarioNew += onScenarioNewCallback
root.NewScenario('ExampleScenario')
# callback should be executed now

# remove the callback from the handler
stkObjectRootEvents.OnScenarioNew -= onScenarioNewCallback

# all finished with events, unsubscribe
stkObjectRootEvents.Unsubscribe()

The STK Desktop application user interface might become unresponsive to user input when Python has event subscribers, and STK tries to call back into the Python interpreter to notify of an event. That callback relies on the Windows message loop to be dispatched. To work around this issue, Windows messages need to be dispatched through the Windows message queue. This can be accomplished in different ways depending on the type of Python script that is executing (console or user interface), and on the type of user interface library being used. For instance, if you use the tkinter user interface library, a simple way of accomplishing this with this library is to create a tkinter window while using the desktop application user interface. No action is needed if Python is used only for automation. The following script is an example showing this issue.

[Python - STK API]
from agi.stk12.stkdesktop import STKDesktop
from agi.stk12.stkobjects import AgESTKObjectType

def onStkObjectAddedCallback(Path:str):
print(f'{Path} has been added.')
stk = STKDesktop.StartApplication(visible=True)
root = stk.Root
root.NewScenario('ExampleScenario')
stkObjectRootEvents = root.Subscribe()
stkObjectRootEvents.OnStkObjectAdded += onStkObjectAddedCallback
sc = root.CurrentScenario

#onStkObjectAddedCallback will be successfully called when the next line is executed
fac = sc.Children.New(AgESTKObjectType.eFacility, 'AGIHQ')

#Now switch control to the desktop application and create another facility.
#The user interface will become unresponsive.

#Now open a tkinter window that processing COM messages.
from tkinter import Tk
window = Tk()
window.mainloop()
#Switch control to the desktop application and create another facility.
#The user interface will be responsive and the event callback will be successful.

STK's Built-in Python Plugins

Starting in version 12.4, STK has multi-platform Python plugins available for some plugin points. You can use these plugin points d by supplying STK with a Python script implementing the plugin interface. For more information about configuring Python plugins, visit the Python plugin documentation.

API modules

Most plugins utilize interfaces in the STK API for getting data from and sending data to STK. Interfaces specific to plugins have been added to agi.stk12.plugins. Using the STK object model within a plugin is possible by obtaining the object root in the Init method of the plugin using IAgStkPluginSite.StkObjectRoot.

File structure

The following example Calc Scalar plugin is used to highlight the structure of a Python plugin using the STK Python API. While the file may have any name, the plugin class name is a requirement (e.g., CAgCrdnCalcScalarPythonPlugin). All methods defined in the plugin interface (e.g., IAgCrdnCalcScalarPythonPlugin) must be defined in the class.

[Python - STK API]
import typing
from agi.stk12.plugins.crdnplugin import IAgCrdnCalcScalarPluginResultReg, IAgCrdnCalcScalarPluginResultReset, IAgCrdnCalcScalarPluginResultEval
from agi.stk12.plugins.utplugin import IAgUtPluginConfig, AgEUtLogMsgType
from agi.stk12.plugins.attrautomation import AgEAttrAddFlags
from agi.stk12.plugins.stkplugin import AgStkPluginSite

# The class name may not be changed
class CAgCrdnCalcScalarPlugin(object):
def __init__(self):
self.scope = None
self.site = None
self.root = None
self.ObjectTrajectoryCatesianX = None

#plugin configuration properties - must be configured in GetPluginConfig for STK to use them
self.scale_factor = 1.23

# Define all methods in IAgCrdnCalcScalarPlugin
def Init(self, site:"IAgUtPluginSite") -> bool:
self.site = AgStkPluginSite(site)
self.root = self.site.StkRootObject # Gain access to STK Object Model for use within the plugin script
self.site.Message(AgEUtLogMsgType.eUtLogMsgInfo, f'{self.display_name} has been initialized by {self.site.SiteName}.')
return True

def Register(self, result:"IAgCrdnCalcScalarPluginResultReg") -> None:
objPath = result.ObjectPath
result.ShortDescription = "Component created on " + objPath

def Reset(self, result:"IAgCrdnCalcScalarPluginResultReset") -> bool:
#<MyObject>, <MySelf>, <MyParent>, <MyGrandParent> are valid keywords for sourceName
self.ObjectTrajectoryCatesianX = result.CalcToolProvider.GetCalcScalar("Trajectory(CBF).Cartesian.X", "<MyObject>")
return True

def Evaluate(self, result:"IAgCrdnCalcScalarPluginResultEval") -> bool:
if self.ObjectTrajectoryCatesianX is not None:
(x, errFlag_x) = self.ObjectTrajectoryCatesianX.CurrentValue_Array(result)
if errFlag_x is not None and errFlag_x!=0:
result.SetValue(x*self.scale_factor)
return True
return False

def Free(self) -> None:
self.scope = None
self.site = None
self.root = None
self.ObjectTrajectoryCatesianX = None

def GetPluginConfig(self, pAttrBuilder:"IAgAttrBuilder") -> typing.Any:
''' Defining GetPluginConfig is only necessary if adding configuration properties accessible to STK '''
if self.scope is None:
self.scope = pAttrBuilder.NewScope()
pAttrBuilder.AddDoubleDispatchProperty( self.scope, "ScaleFactor", "A double used to scale the plugin evaluation result", "scale_factor", AgEAttrAddFlags.eAddFlagNone )
return self.scope

def VerifyPluginConfig(self, pPluginCfgResult:"IAgUtPluginConfigVerifyResult") -> None:
pass

Configuration

The above example specified a user configuration property called "ScaleFactor" that will be accessible from STK. If two or more instances of the above Calc Scalar plugin are instantiated, each instance will have its own unique value for the configuration property. AGI recommends that any name used for these configuration properties not include spaces because certain interfaces to the properties may not work correctly.

Adding properties for Python plugins

If a plugin interface defines a property (see IAgAccessConstraintPlugin for an example), the property must also be defined in the class. There are two ways the STK Python API supports the user in doing so.

The first way is to define the property as a member of the plugin class. In the example below, the property from the link above is seen being defined. The name of the member matches the name of the property from the link, as this is necessary for the STK Python API to retrieve its value.

[Properties - Example 1]
class CAgAccessConstraintPlugin(object):
def __init__(self):
self.DisplayName = 'PythonRangeExample'

The second way to define the property is using the @property decorator on a method with the property name. The return type must match the type defined by the property.

[Properties - Example 2]
class CAgAccessConstraintPlugin(object):

@property
def DisplayName(self) -> str:
return 'PythonRangeExample'

For the full implemented example of the property being defined for the IAgAccessConstraintPlugin interface, see the Code Samples folder.

Hidden parameters for the Object Model

Parameter Description
root The STK Object Model root / starting point for the API. The usual way of getting the Object Model root via a Python script doesn't work here since you need the running root context. The parameter to use versionString = root.ExecuteCommand('GetStkVersion /')
abortMCS boolean, initial value is False. "Output value" i.e. STK reads this after the script executes. Set to true and the MCS sequence stops.
iteration integer number of the sequence iteration. "input only" i.e. STK doesn't read this back. e.g. if integer > 100: abortMCS=True

gRPC Specific Features

The methods SetGrpcOptions and NewGrpcCallBatcher were added to STK Application interfaces: agi.stk12.stkruntime.STKRuntimeApplication, agi.stk12.stkdesktop.STKDesktopApplication, agi.stk12.stkengine.STKEngineApplication. While gRPC is not available for use with STK Engine, the methods are provided for parity with the other two application interfaces.

gRPC Keyword Options

Keyword options are passed to the method SetGrpcOptions in the form of a dictionary of keyword value pairs. The available options are:

Keyword Value Type Default Description
release batch size int 12 Release batching is a form of garbage-collection added to reduce the communication over the gRPC server. The destruction of any STK Object Model interface object results in a 'release' call. Batching these calls together for a certain number of object results in reduced communications to the server. Setting the value to one (1) disables this feature.
collection iteration batch size int 100 When iterating a collection using the syntax 'for object in collection:...', the values are requested in batches rather than as each iteration occurs to reduce the communication over the gRPC server. Setting the value to one (1) disables batching for collection iteration.
disable batching bool False Disables batching of all agi.stk12.utilities.grpcutilities.GrpcCallBatcher objects. Setting this value may be useful for debugging since call-batching results in asynchronous error messages.
raise exceptions with STK Engine bool True By default, calling agi.stk12.stkengine.STKEngineApplication.SetGrpcOptions or agi.stk12.stkengine.STKEngineApplication.NewGrpcCallBatcher raises an exception because these operations are not active with STK Engine. This option suppresses those exceptions.

gRPC Call Batcher

Calling the application method NewGrpcCallBatcher returns an object of type agi.stk12.utilities.grpcutilities.GrpcCallBatcher. This feature is provided as an option to reduce the communication over the gRPC server in performance-critical applications. API calls may be batched together and sent to STK in one remote procedure call if no return value is needed from the calls.

Activating batching causes the normal API exception behavior to be altered. Exceptions from one command may appear asyncronously. Therefore, it is not recommended to use call batching while building and debugging, but rather as a performance optimization.

Only calls that do not return a value may be batched together, such as set-property requests and methods without a return value. Any method that has a return value (including get-property requests) automatically execute any previously batched commands before the method with a return value is executed.

Therefore, to reduce the number of remote API requests and improve performance, code must be organized to group together commands that do not have a return value. Call chaining interrupts a batch request because of the get-property command within the chain. e.g.:

[Python - STK API]
root.CurrentScenario.ShortDescription = short_description
root.CurrentScenario.LongDescription = long_description

The above example won't be batched together because the call to CurrentScenario gets the scenario via an API call. These commands may be batched by factoring out the call chaining. e.g.:

[Python - STK API]
scen = root.CurrentScenario
scen.ShortDescription = short_description
scen.LongDescription = long_description

This class may be used via the explicit commands or by using the with statement to batch together the commands within the statement block. e.g.:

[Python - STK API]
call_batcher = stk.NewGrpcCallBatcher()
with call_batcher:
facility1.LocalTimeOffset = 1.0
facility1.HeightAboveGround = 10.0
facility1.UseLocalTimeOffset = True
facility1.ResetAzElMask()

gRPC Futures

gRPC Futures are included to expand the use cases of the gRPC Call Batcher and further reduce communication over the gRPC server in performance-critical applications. gRPC Futures are created in place of an API method call that returns some STK API interface. However, unless the method call, creating the future does not force execution of the call batch. Only when a return value is requested from the future does the future "bind" and force execution of the call batch. Futures are created with agi.stk12.utilities.grpcutilities.GrpcCallBatcher.create_future.

To create a future, the method object, return type, and the method arguments are needed. The following example creates 100 facilities and adds them to a constellation, without causing the GrpcCallBatcher to execute. All of the operations are sent to STK to be executed at the end of the snippet.

[Python - STK API]
scen_children = root.CurrentScenario.Children
constellation = scen_children.New(AgESTKObjectType.eConstellation, "Constellation1")
constellation_objects = con.Objects
with stk.NewGrpcCallBatcher() as batcher:
for i in range(100):
fac_future = batcher.create_future(scen_children, IAgStkObjectCollection.New, AgFacility, AgESTKObjectType.eFacility, f"Facility{i}")
fac_future.HeightAboveGround = 10.0
constellation_objects.AddObject(fac_future)