Geosoft Project
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:
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:
property | meaning |
---|---|
name | project name project file name |
gid | Geosoft ID of the user |
project_databases | 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.
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 5 | This 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 7 | The 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 10 | Here we use the project property project_grids, which is a list of the grid data sets contained in this project. |
line 21 | This shows how to ask the user to identify the grid to process from a list of the grids in the project. |
line 27 | We ask for an output grid file name. The filemask="*.grd" will present a browse tool that supports all supported grid types. |
Line 33-35 | We 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 34 | The new grid needs to have the same geometric properties as the starting grid, which is accomplished by properties=g_input.properties(). |
Line 38 | The 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:
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 8 | We 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 14 | This 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 22 | Entry point when run as an extension. |
line 25 | Get 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 28 | The 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 32 | Report 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:
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:
python.exe grid_statistics.py grid file? elevation.grd minimum: 88.0616683959961 maximum: 339.3451843261719 mean: 167.95299033603075 standard deviation: 61.818035062225405
line 2 | Provide 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:
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 1 | We need the Python os module to get the python file basename in line 29, which we will use as the parameter group name. |
---|---|
line 30 | Parameters 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 35 | Here we set the default entry in the dialog to the saved parameter. |
line 40 | The 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. |