Geosoft Project

What you will learn

  1. What is a Geosoft Project.
  2. How to run a Python script from an open project.
  3. How to get information about the state of an open project.
  4. How to interact with the open application.
  5. Create a script that will run both as an extension and stand-alone.
  6. How to save user input to become the default.

Geosoft Project

The Geosoft Desktop applications (Oasis montaj, Target and the Viewer) all work inside the context of a named project. The project maintains the knowledge of all the data within a project, which it exposes to the user through the Project Explorer, and all the data sets and tools that are open in various windows. The project also maintains information about the user's preferences for a particular project, and the processing history and dialog settings for all the tools that the user has applied within the project.

A Geosoft Project is stored on a file system in a file with extension .gpf.

Python access to a project is possible when a Python script is run from a Geosoft application that has a project open, or from a Geosoft script. The exercises in this section all assume you have a Target or Oasis montaj open with an active project. The tutorial folder includes geosoft_project.gpf, which you can use to run these exercises.

Running a Python script from an open project

The Geosoft Project exercises included a section that showed how you can run a script from an open project. To recap, see:

Geosoft Project#HelloWorld-ConfiguringGeosoftDesktoptorunPythonextensions

Geosoft Project#HelloWorld-hello_world_extension.py

A script run from an open project requires a function named rungx(), which is the entry point for your script.  The context of the project is already established and available to your script.  For example, the following simple script displays the project name and the Geosoft ID of the user, which are both properties of the project:


geosoft_project.py
import geosoft.gxpy.project as gxpj

def rungx():
    project = gxpj.Geosoft_project()
    gxpj.user_message("Project name: {}".format(project.name),
                      "Project user: {}".format(project.gid))

The project has many properties that allow you to determine the state of the open project.  See the geosoft.gxpy.Geosoft_project sub-module documentation for a complete list of properties, but here are a few:

propertymeaning

name
project_file

project name
project file name
gidGeosoft ID of the user

project_databases
project_grids
project_voxels
project_3dv
project_maps
project_voxi_models
project_gmsys_2d
project_gmsys_3d

Data set lists
open_grids
open_maps
open_databases
etc...
Open data set windows
current_database
current_map
current_grid
current_voxel
etc...
Data set that has focus for each data set type.

Process a grid file from the project

A common task is to open a project data set, perform some function on the data and save the modified data. This exercise provides a pattern for processing data sets contained in a Geosoft project. In this example we will demonstrate some simple user interaction to allow a user to apply a first virtical derivative to a grid and save the result in a new grid data set.

grid_vertical_derivative.py
import geosoft.gxpy.project as gxpj
import geosoft.gxpy.grid as gxgrd
import geosoft.gxapi as gxapi

def rungx():

    project = gxpj.Geosoft_project()

    # there must be grids in the project
    if len(project.project_grids) == 0:
        raise Exception('This project contains no grids.')

    # default grid will be the current grid, or the first grid in the list of project grids
    if project.current_grid:
        default_grid = project.current_grid
    else:
        default_grid = project.project_grids[0]

    # ask the user to select a grid
    grid_name = gxpj.get_user_input(title='Vertical derivative of a grid',
                                    prompt='Grid to process',
                                    kind='list',
                                    items=project.project_grids,
                                    default=default_grid)

    # ask for a new grid file name
    new_grid = gxpj.get_user_input(title='Vertical derivative of a grid',
                                   prompt='Output vertical derivative grid name',
                                   kind='file',
                                   filemask='*.grd')

    # calculate vertical derivative
    with gxgrd.Grid(grid_name) as g_input:
        with gxgrd.Grid.new(new_grid, properties=g_input.properties(), overwrite=True) as g_output:
            gxapi.GXIMU.grid_vd(g_input.gximg, g_output.gximg)

    # open the vertical derivative grid
    gxpj.add_document(new_grid)
line 5This script can only be run from a Geosoft desktop application, either by using the "Run GX...", or from a Geosoft menu that contains the name of the script as the menu action.. The rungx() function is the entry point.
line 7The Geosoft_project instance is a singleton, in that there can only be one project. Also, the project can only be instantiated from a script run from a Geosoft desktop application that has an open project. The Geosoft_project instance contains context for everything that the user sees within a project.
line 10Here we use the project property project_grids, which is a list of the grid data sets contained in this project.
line 21This shows how to ask the user to identify the grid to process from a list of the grids in the project.
line 27We ask for an output grid file name. The filemask="*.grd" will present a browse tool that supports all supported grid types.
Line 33-35We will use the geosoft.gxapi.GXIMU.grid_vd() function to calculate the first vertical derivative of a grid. This function requires GXIMG instances of both the input and the output grid, and this shows how to use Python's with... construct to work with grids. Note the gximg property of a grid instance is the GXIMG instance expected for any gxapi method that works with grid objects.
Line 34The new grid needs to have the same geometric properties as the starting grid, which is accomplished by properties=g_input.properties().
Line 38The new grid is added to the project, which will, by default, open the grid in a grid viewer window.

Simple User Input

We will create an extension to calculate and report basic grid statistics for a grid file specified by the user.  

The geosoft.gxpy.project sub-module contains methods and functions that work with a project open in a Geosoft Desktop application, such as Oasis montaj.  The get_user_input() function uses the GX UI capabilities to display a simple dialog to the user:

grid_statistics.py
import geosoft.gxapi as gxapi
import geosoft.gxpy.gx as gx
import geosoft.gxpy.grid as gxgrid
import geosoft.gxpy.project as gxproj


# function to calculate grid statistics
def grid_stats(grid_file):

    # create a gxapi.GXST instance to accumulate statistics
    stats = gxapi.GXST.create()

    # open the grid and add each grid row to the stats instance
    with gxgrid.Grid.open(grid_file) as grid:
        for row in range(grid.ny):
            stats.data_vv(grid.read_row(row).gxvv)

    return stats


# entry point when run from a Geosoft Desktop application
def rungx():

    # get the name of a grid from the user
    grid_file = gxproj.get_user_input(title='Grid statistics',
                                      prompt='Grid file',
                                      kind='file',
                                      filemask='*.grd')

    stats = grid_stats(grid_file)

    gxproj.user_message(grid_file,
                        'min: {}\nmax: {}\nmean: {}\nstd_dev: {}'.format(stats.get_info(gxapi.ST_MIN),
                                                                         stats.get_info(gxapi.ST_MAX),
                                                                         stats.get_info(gxapi.ST_MEAN),
                                                                         stats.get_info(gxapi.ST_STDDEV)))
line 8We create a function to calculate and return a statistics object from a grid.  This will be re-used in the next section to support running this script as a stand-alone program.
line 14This shows how to open a grid using the Python construct with ... as ...:.  Most Geosoft classes support this construct, which ensures that instance resources are cleaned-up correctly when the class instance goes out context.
line 22Entry point when run as an extension.
line 25Get the grid file name from the user (see https://geosoftinc.github.io/gxpy/9.3/python/geosoft.gxpy.project.html#module-geosoft.gxpy.project).
line 28The file mask "*.grd" is interpreted as a Geosoft grid type, which will display a browse dialog that allows the user to select any supported grid type.  Grid file names are returned in their correctly decorated form and can be passed directly to the geosoft.gxpy.grid.Grid.open() method (line 14).
line 32Report grid statistics to the user.


Running this script from Oasis montaj will produce the following:

Use the browse button to select a grid file:


Adding support for running as a stand-alone script

You may need to create a script that will run both as an extension, within the context of an open project, or as a stand-alone program.  To do this, separate the user-interface functionality from the actual work functionality.  In this example, the user interface work gets the name of a grid to process and reports the grid statistics.  The work is to calculate the statistics from the user-identified grid.

For an extension, the user interface work should be placed in the rungx() function, and the stand-alone script work placed in the Python standard if __name__ == "__main__"section, as follows:

grid_statistics.py
import geosoft.gxapi as gxapi
import geosoft.gxpy.gx as gx
import geosoft.gxpy.grid as gxgrid
import geosoft.gxpy.project as gxproj


# function to calculate grid statistics
def grid_stats(grid_file):

    # create a gxapi.GXST instance to accumulate statistics
    stats = gxapi.GXST.create()

    # open the grid and add each grid row to the stats instance
    with gxgrid.Grid.open(grid_file) as grid:
        for row in range(grid.ny):
            stats.data_vv(grid.read_row(row).gxvv)

    return stats


# entry point when run from a Geosoft Desktop application
def rungx():

    # get the name of a grid from the user
    grid_file = gxproj.get_user_input(title='Grid statistics',
                                      prompt='Grid file',
                                      kind='file',
                                      filemask='*.grd')

    stats = grid_stats(grid_file)

    gxproj.user_message(grid_file,
                        'min: {}\nmax: {}\nmean: {}\nstd_dev: {}'.format(stats.get_info(gxapi.ST_MIN),
                                                                         stats.get_info(gxapi.ST_MAX),
                                                                         stats.get_info(gxapi.ST_MEAN),
                                                                         stats.get_info(gxapi.ST_STDDEV)))


# running as stand-alone program
if __name__ == "__main__":

    # create context
    gxc = gx.GXpy()

    stats = grid_stats(input('grid file? '))

    # print statistical properties
    print('minimum: ', stats.get_info(gxapi.ST_MIN))
    print('maximum: ', stats.get_info(gxapi.ST_MAX))
    print('mean: ', stats.get_info(gxapi.ST_MEAN))
    print('standard deviation:', stats.get_info(gxapi.ST_STDDEV))

Running this script directly from the Python interpreter:

command-line execution
python.exe grid_statistics.py
grid file? elevation.grd
minimum:  88.0616683959961
maximum:  339.3451843261719
mean:  167.95299033603075
standard deviation: 61.818035062225405
line 2Provide the grid file name.  On this example we have not provided the decoration since the default is a Geosoft grid, but we would have entered elevation.grd(GRD) to produce the same result.

Remembering the last user input

A natural behaviour in Geosoft software is to pre-populate dialogs with the user entries from the last time a particular dialog was run. This supports a natural experience as one is either re-running a particular tool and only changing certain parameters, or the user has preferences that can be remembered as part of a project.

To accomplish this a Geosoft project can store named parameters as part of the project, and these can be retrieved and re-used the next time a particular script is run.  Named parameter sets have a group name and one or more parameter names. The group name must be unique to the script, and we recommend using the Python script base name as this is most likely to be unique within the context of any project.

In this example, we will save the grid file name in parameter "GRID_FILE", part of the "GRID_STATISTICS_REMEMBER_USER_INPUT" group, which is the base name of the Python script file:

grid_statistics_remember_user_input.py
import os
import geosoft.gxapi as gxapi
import geosoft.gxpy.grid as gxgrid
import geosoft.gxpy.project as gxproj
import geosoft.gxpy.utility as gxu


# function to calculate grid statistics
def grid_stats(grid_file):

    # create a gxapi.GXST instance to accumulate statistics
    stats = gxapi.GXST.create()

    # open the grid
    grid = gxgrid.Grid.open(grid_file)

    # add data from each row to the stats instance
    for row in range(grid.ny):
        stats.data_vv(grid.read_row(row).gxvv)

    return stats


# entry point when run from a Geosoft Desktop application
def rungx():

    # parameter 'GRID_FILE' is the last-specified grid file name for this script.
    grid_parameter = 'GRID_FILE'
    group = os.path.basename(__file__).split('.')[0]
    parms = gxu.get_parameters(group, {grid_parameter: ''})

    # get the name of a grid from the user
    grid_file = gxproj.get_user_input(title='Grid statistics',
                                      prompt='Grid file',
                                      default=parms.get(grid_parameter),
                                      kind='file',
                                      filemask='*.grd')

    # save the grid file name as the default the next time this script is run
    parms[grid_parameter] = grid_file
    gxu.save_parameters(group, parms)

    stats = grid_stats(grid_file)

    gxproj.user_message(grid_file,
                        'min: {}\nmax: {}\nmean: {}\nstd_dev: {}'.format(stats.get_info(gxapi.ST_MIN),
                                                                         stats.get_info(gxapi.ST_MAX),
                                                                         stats.get_info(gxapi.ST_MEAN),
                                                                         stats.get_info(gxapi.ST_STDDEV)))
line 1We need the Python os module to get the python file basename in line 29, which we will use as the parameter group name.
line 30Parameters are exposed as Python dictionaries, and in this line we both establish the dictionary entries we want and their initial default values.  You can, of course, have any number of parameters, but for this example we only need one which we name 'GRID_FILE'.  Parameters are case insensitive, so 'GRID_FILE' and 'grid_file' will be stored as the same parameter, but if you call gxu.get_parameters(group), the parameter dictionary that is returned will have all upper-case parameter names.
line 35Here we set the default entry in the dialog to the saved parameter.
line 40The returned user grid file name is replaced in the parameter dictionary, which is then saved.  The next time this script is run the saved parameter will be recovered and used.