Ocean Developer's Guide - Ocean - Schlumberger
Ocean Developer's Guide - Ocean - Schlumberger
Ocean Developer's Guide - Ocean - Schlumberger
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
Getting Started with <strong>Ocean</strong><br />
For Geoscientists and Software Developers<br />
Published by <strong>Schlumberger</strong> Information Solutions,<br />
5599 San Felipe, Houston Texas 77056
Getting Started with <strong>Ocean</strong><br />
Copyright Notice<br />
Copyright © 2008 <strong>Schlumberger</strong>. All rights reserved.<br />
The information in this document is subject to change without notice. The<br />
software described in this document is furnished under a license agreement.<br />
This software may be used or copied only in accordance with the terms of<br />
such agreement. It is against the law to copy the software on any medium<br />
except as specifically allowed in the license agreement. No part of this<br />
document may be reproduced or transmitted in any form, or by any means,<br />
electronic or mechanical, including photocopying and recording, for any<br />
purpose without the express written permission of <strong>Schlumberger</strong>.<br />
Trademarks<br />
Petrel and <strong>Ocean</strong> are trademarks of <strong>Schlumberger</strong>.<br />
Microsoft® and Windows® are registered trademarks of Microsoft<br />
Corporation.<br />
ii
Table of Contents<br />
Chapter 1 Welcome to <strong>Ocean</strong> ...................................................................... 1<br />
Introduction .................................................................................................................. 1<br />
Access to Petrel Data Domain ................................................................................. 4<br />
<strong>Ocean</strong> UI Infrastructure ............................................................................................. 6<br />
The <strong>Ocean</strong> Module ..................................................................................................... 7<br />
IModule Interface .................................................................................................. 9<br />
Chapter 2 Your First Application ............................................................... 12<br />
Writing the Application ............................................................................................ 12<br />
Starting with Visual Studio ................................................................................. 13<br />
Inspect the Files ................................................................................................... 14<br />
Write Algorithm Code .......................................................................................... 18<br />
Running the Application .......................................................................................... 20<br />
Chapter 3 The Petrel Data Domain ............................................................ 24<br />
<strong>Ocean</strong> Exposes Petrel’s Data Model ..................................................................... 24<br />
Entities and Properties ........................................................................................ 24<br />
User View of the Petrel Data Model ................................................................. 26<br />
Data Access ............................................................................................................... 32<br />
Data Types Exposed ............................................................................................ 33<br />
Read Access .............................................................................................................. 38<br />
Browsing through collections ............................................................................ 38<br />
Seismic ................................................................................................................... 39<br />
Well and Geology ................................................................................................. 40<br />
Static Model .......................................................................................................... 42<br />
Simulation and Data Analysis ............................................................................ 43<br />
Data Updates ............................................................................................................. 44<br />
Transactions.......................................................................................................... 45<br />
Modifying Domain Objects ................................................................................. 45<br />
Domain Object Relationships ............................................................................. 46<br />
Domain Object Creation ........................................................................................... 48<br />
iii
Table of Contents<br />
Creating New Instances ..................................................................................... 48<br />
Creating New Collections ................................................................................... 49<br />
Deletions ..................................................................................................................... 52<br />
Data Access Example .............................................................................................. 52<br />
Online Manual for Data Domain ............................................................................. 55<br />
Opening the manual ............................................................................................. 55<br />
Using Intellisense ................................................................................................. 56<br />
Class Definitions ................................................................................................... 57<br />
Chapter 4 Extending the Petrel UI ............................................................. 60<br />
How to Add a New Menu Item ............................................................................... 60<br />
Edit IntegratePresentation method ................................................................... 61<br />
Creating UI Tools .................................................................................................. 61<br />
Define the Menu Item Properties ...................................................................... 62<br />
Add to ToolsManager .......................................................................................... 64<br />
Results ........................................................................................................................ 64<br />
Chapter 5 Extending the Data Domain ...................................................... 66<br />
Basic Custom Domain Object ................................................................................. 67<br />
Adding to Input and Models Trees ........................................................................ 68<br />
Adding to Native Petrel Domain Objects ......................................................... 69<br />
Adding an Object from a Context Menu ........................................................... 70<br />
Customizing Tree Presentation .......................................................................... 72<br />
Object Visualization .................................................................................................. 74<br />
3D Window Display .............................................................................................. 74<br />
Map Window Display .......................................................................................... 77<br />
Saving Custom Domain Objects ............................................................................. 81<br />
Serialization ........................................................................................................... 81<br />
iv
Chapter 1<br />
Welcome to <strong>Ocean</strong><br />
Introduction<br />
<strong>Ocean</strong> is an application development framework with the capability<br />
to work across data domains. It provides services, components,<br />
and a common graphical user interface that enables applications to<br />
better integrate. It also allows application developers to interact<br />
with other <strong>Ocean</strong> Model-Centric applications such as Petrel.<br />
<strong>Ocean</strong> Model-Centric applications are loaded dynamically as .NET<br />
assemblies. These assemblies, the building blocks of <strong>Ocean</strong>,<br />
contain Modules.<br />
The <strong>Ocean</strong> architecture has three levels: the Core, the Services and<br />
the product family. For Model-Centric applications, the product<br />
family is Petrel. <strong>Ocean</strong> modules are managed by the Core layer.<br />
They interact with all levels of the framework, as shown below:<br />
1
Welcome to <strong>Ocean</strong><br />
Application Module<br />
Deployed as plug-in or extension module<br />
<strong>Schlumberger</strong> or 3rd party<br />
Product Family<br />
<strong>Ocean</strong> Services<br />
<strong>Ocean</strong> Core<br />
Figure 1-1 - <strong>Ocean</strong> Architecture<br />
The <strong>Ocean</strong> Core plays the role of the basic infrastructure. It<br />
manages <strong>Ocean</strong> modules, registers services, both the services preloaded<br />
by the product family and that are added dynamically via the<br />
API. The <strong>Ocean</strong> Core manages the data sources provided by the<br />
product family or any external data source that could be defined by<br />
any module. It also performs event management and basic<br />
message logging.<br />
The <strong>Ocean</strong> Services are a set of application independent utilities.<br />
They are modules that benefit from being standardized across<br />
product families. An example is the Coordinate Service – a utility for<br />
converting between projection and geodetic co-ordinate systems.<br />
The <strong>Ocean</strong> Services layer only depends on the <strong>Ocean</strong> Core or on<br />
other <strong>Ocean</strong> Services.<br />
2
Welcome to <strong>Ocean</strong><br />
The product family is the host for <strong>Ocean</strong> applications, and is the<br />
environment the <strong>Ocean</strong> module needs to run in. The product family<br />
provides:<br />
• the domain objects and their data source<br />
• the graphical environment in which the applications will<br />
display their data<br />
• a common look and feel for all application user interface<br />
components<br />
Application modules connect to all software layers as well as to the<br />
.NET framework. Application modules can register their own<br />
services with the <strong>Ocean</strong> Core and benefit from services registered<br />
by other modules. All applications built on the <strong>Ocean</strong> framework are<br />
designed in a similar fashion but they rely on a product family to<br />
build and run.
Welcome to <strong>Ocean</strong><br />
Access to Petrel Data Domain<br />
The application can access data in the following domains provided<br />
by <strong>Ocean</strong>:<br />
Well (for Petrophysics and Geology applications)<br />
Seismic (for Geophysics)<br />
Shapes (for Structural modeling)<br />
PillarGrid (for Geomodeling)<br />
Simulation (for Reservoir Evaluation)<br />
4
Figure 1-2 Surface with fault model and seismic data<br />
Welcome to <strong>Ocean</strong>
Welcome to <strong>Ocean</strong><br />
<strong>Ocean</strong> UI Infrastructure<br />
6<br />
<strong>Ocean</strong> provides the capability to extend Petrel’s user interface<br />
functionality. The <strong>Ocean</strong> Application Programming Interface (API)<br />
supports:<br />
• Windows: adding custom windows.<br />
• Renderers:<br />
o Adding renderers for domain objects (native and<br />
custom) in different windows.<br />
• Interactions:<br />
o Adding custom window modes to define interactions<br />
in different windows.<br />
o Object picking in different windows for its<br />
manipulation.<br />
• Menus and toolbars:<br />
o Adding new menus to Petrel window or extending<br />
Petrel menus.<br />
o Adding new toolbars with custom tools<br />
o Extending Petrel toolbars with custom tools.<br />
• Petrel project explorer:
Welcome to <strong>Ocean</strong><br />
o Adding custom objects in Petrel tree in a particular<br />
hierarchy.<br />
o Adding Processes and workflows in Petrel process<br />
diagram and Workflow editor.<br />
The <strong>Ocean</strong> Module<br />
An <strong>Ocean</strong> module is an extension to the product family. Application<br />
programmers create modules that behave just like any standard<br />
part of Petrel. The modules are compiled into assemblies that are<br />
placed in the Petrel installation directory and registered with the<br />
application through the configuration file. Petrel uses the<br />
configuration file to load all registered modules during startup.
Welcome to <strong>Ocean</strong><br />
Figure 1-3 - <strong>Ocean</strong> Modules<br />
The <strong>Ocean</strong> module has a defined lifecycle with certain<br />
requirements and restrictions that allow the clean integration into<br />
the product family. Its lifecycle phases serve to license the module,<br />
initialize and integrate it into the product family, add presentation<br />
interfaces, and remove all these when the module is unloaded. The<br />
appearance and interaction is seamlessly allows the product family<br />
to treat the module as native code.<br />
An <strong>Ocean</strong> module uses the functionalities provided by the product<br />
family, the <strong>Ocean</strong> Services, the <strong>Ocean</strong> Core, and also the .NET<br />
architecture. It may also use 3rd party application assemblies and<br />
other modules.<br />
<strong>Ocean</strong> modules are built with Visual Studio 2005 for .NET with the<br />
help of the <strong>Ocean</strong> wizard. The wizard takes care of the interaction<br />
with the Core and some low-level access to functionality provided<br />
by the product family.<br />
8
Welcome to <strong>Ocean</strong><br />
IModule Interface<br />
An <strong>Ocean</strong> module implements the IModule interface. The product<br />
family is responsible for loading the module. IModule is defined in<br />
the Slb.<strong>Ocean</strong>.Core namespace.<br />
The IModule interface defines five methods of the module<br />
lifecycle phases and inherits from IDisposable. The phases and<br />
their methods are:<br />
• construction (default constructor)<br />
• initialization (Initialize)<br />
• integration (Integrate)<br />
• presentation integration (IntegratePresentation)<br />
• disintegration (Disintegrate)<br />
• disposal (Dispose)<br />
Default Constructor<br />
During the product startup the <strong>Ocean</strong> Core will load modules as<br />
defined in the configuration file for the product family. The <strong>Ocean</strong><br />
Core will call the default constructor to instantiate each module.<br />
The constructor could be used to acquire any licenses, initialize any<br />
private fields and acquire any resources the module needs.<br />
Initialization<br />
The second phase of the module lifecycle is the execution of the<br />
Initialize method. The main purpose of Initialize is<br />
registration of the services provided by the module with the <strong>Ocean</strong><br />
ServiceLocator class. When initialization is complete the
Welcome to <strong>Ocean</strong><br />
services registered are available for consumption by all <strong>Ocean</strong><br />
modules.<br />
Integrate<br />
The Integrate method is the first point at which a module may<br />
consume services registered with <strong>Ocean</strong>. This is because all<br />
modules have been through the initialize phase where their<br />
services are registered with <strong>Ocean</strong>. The <strong>Ocean</strong> Core<br />
ServiceLocator class is used to look up services.<br />
Integrate Presentation<br />
The IntegratePresentation method of IModule is the phase<br />
in the module lifecycle in which user interface components (menus,<br />
toolbars, context menus, windows etc.) are added to the product<br />
family.<br />
Disintegrate<br />
When the product family begins to shut down the IModule<br />
Disintegrate method is called on each module. Disintegrate<br />
has the responsibility of cleaning up any presentation elements<br />
installed by the module.<br />
Dispose<br />
Dispose comes from the IDisposable pattern and must be<br />
implemented as part of your IModule implementation. The purpose<br />
of Dispose is to free any unmanaged resources and free any<br />
licenses acquired by the module.<br />
10
Welcome to <strong>Ocean</strong><br />
Example<br />
The following is an example module.<br />
public class MyModule : IModule<br />
{<br />
// Perform initializations, acquire licenses if any<br />
public Module ()<br />
{<br />
...<br />
}<br />
// Initialize services provided by the module<br />
public Initialize ()<br />
{<br />
// Create the service<br />
object FooBar = new FooBarService();<br />
}<br />
// Add the service by its type<br />
CoreSystem.Services.AddService(typeof(FooBarService), FooBar);<br />
...<br />
// Integrate services provided by other modules<br />
public Integrate ()<br />
{<br />
// Find the coordinate system service<br />
ICoordSysService coordService;<br />
coordService = CoreSystem.GetService() as<br />
ICoordSysService;<br />
...<br />
}<br />
// Add to user interface of product family<br />
public IntegratePresentation ()<br />
{<br />
// Define the button to add<br />
ToolbarItem btn = new ToolbarItem(WellKnownUI.Menus.Insert,<br />
“MyButton”, ButtonClick);<br />
}<br />
// Add the button to the Insert menu of the Petrel menubar<br />
PetrelSystem.Tools.Add(btn);<br />
...<br />
// Remove components added to the user interface. Remove services<br />
public Disintegrate ()<br />
{<br />
// Remove the button added to the Insert menu<br />
PetrelSystem.Tools.Remove(btn);<br />
}<br />
// Remove our service<br />
CoreSystem.Services.RemoveService(typeof(FooBarService));<br />
...<br />
// Dispose of any unmanaged resources<br />
public Dispose ()<br />
{<br />
...<br />
}
Your First Application<br />
<strong>Ocean</strong> allows application developers to extend the Petrel<br />
functionality with new custom processes and workflows. In this<br />
chapter, we will walk through the procedure of creating a simple<br />
<strong>Ocean</strong> process.<br />
Writing the Application<br />
For our first application we will write an <strong>Ocean</strong> module that adds a<br />
process to Petrel. The process will print the names of all Seismic<br />
cubes in the current project.<br />
A Process in Petrel is an interactive operation performed on data.<br />
The Process is available from the Petrel Process diagram. The<br />
operation could create new data or update existing data. For<br />
example, the operation of using a set of points to create a well path<br />
would be referred to as a process. In this example the set of points<br />
is the input and the well path is the output.<br />
A Workstep is a single processing step. It differs from a Process in<br />
that it does not require manual interaction when executing. This is<br />
why it can take part in a workflow. Worksteps are available from<br />
12
Your First Application<br />
the Petrel Workflow editor; they also take input data and<br />
parameters, perform some processing, and produce output data.<br />
A Process can be created from a Workstep, so you can write code<br />
one time that is available in both the Process diagram and the<br />
Workflow editor.<br />
We will use the <strong>Ocean</strong> Wizard to create an <strong>Ocean</strong> process from a<br />
workstep for our first application.<br />
These are the steps to create your first application:<br />
1. Run the <strong>Ocean</strong> Wizard in Visual Studio to create the<br />
application module and process.<br />
2. Inspect the files created by the Wizard.<br />
3. Modify the code to add the processing logic.<br />
For details on the installation and use of the <strong>Ocean</strong> Wizard please<br />
refer to the <strong>Ocean</strong> Wizard Help manual.<br />
For details on processes and worksteps, please see the Workflow<br />
chapter in the <strong>Ocean</strong> Developers <strong>Guide</strong>.<br />
Starting with Visual Studio<br />
Start up Visual Studio.<br />
Create a new project using File-> New Project option. Select <strong>Ocean</strong><br />
Petrel Module template from <strong>Ocean</strong> under Visual C# project type.
Your First Application<br />
<strong>Ocean</strong><br />
Project<br />
Type<br />
<strong>Ocean</strong> Module<br />
Project Name<br />
Create an <strong>Ocean</strong> module with a process that has no arguments and<br />
an auto-generated UI.<br />
Provide the name “ListSeismic” for the project. It’s generally a good<br />
practice to use the name “Module” for the application module.<br />
Add a new process and add to the process a workstep with the<br />
name “ListSeismicCubes” with its own process wrapper. See the<br />
<strong>Ocean</strong> Wizard help for details. Do not specify any workstep<br />
argument.<br />
Inspect the Files<br />
The <strong>Ocean</strong> Wizard will create a solution named “ListSeismic” with<br />
a project named “ListSeismic” in the Visual Studio Solution<br />
Explorer. The project will contain source files for the Module class<br />
14
Your First Application<br />
that was created, and the ListSeismicCubes process that is added<br />
to the Process diagram.<br />
workstep<br />
source file<br />
module<br />
source file<br />
Figure 0-1: Example project source files in Solution Explorer<br />
The wizard configures the ListSeismic project to help debugging.<br />
The assembly is output to the Petrel installation directory. The<br />
project command line property is set up to start Petrel. So<br />
debugging is simply a matter of building and starting the debugger.<br />
The wizard writes the basic code needed for the ListSeismicCubes<br />
application. We will need to add code for our specialized algorithm<br />
in order to print the names of all Seismic Cubes in the current<br />
project.<br />
Module<br />
The Module class implements the IModule interface. Our new<br />
process is added to the Process manager in the Integrate<br />
method automatically by the Wizard.<br />
First the new ListSeismicCubes workstep is created.<br />
public void Integrate()<br />
{<br />
// Registrations:<br />
ListSeismicCubes sWorkstep = new ListSeismicCubes();
Your First Application<br />
Then it is added to the Workflow editor.<br />
PetrelSystem.WorkflowEditor.Add(sWorkstep);<br />
Finally, the process is created from the workstep using the<br />
WorkstepProcessWrapper convenience class. The new<br />
process is added to the Process diagram using the<br />
PetrelSystem.ProcessDiagram API.<br />
IProcessDiagram procDiag = PetrelSystem.ProcessDiagram;<br />
procDiag.Add(new WorkstepProcessWrapper(sWorkstep),"Plugins");<br />
}<br />
Putting the pieces together, the wizard generated the following<br />
Integrate method:<br />
public void Integrate()<br />
{<br />
// Registrations:<br />
ListSeismicCubes sWorkstep = new ListSeismicCubes();<br />
PetrelSystem.WorkflowEditor.Add(sWorkstep);<br />
IProcessDiagram procDiag = PetrelSystem.ProcessDiagram;<br />
procDiag.Add(new WorkstepProcessWrapper(sWorkstep),"Plugins");<br />
}<br />
ListSeismicCubes<br />
16<br />
The ListSeismicCubes class is the workstep class that holds<br />
the processing algorithm. We saw how a process was created<br />
from it in the module.<br />
ListSeismicCubes derives from Workstep, a generic class<br />
that includes the description of the arguments.<br />
public abstract class Workstep : Workstep<br />
where TArgPack : new ();<br />
{<br />
protected abstract void CopyArgumentPackageCore (<br />
TArgPack fromArgPackage,<br />
TArgPack toArgPackage);<br />
protected virtual TArgPack CreateArgumentPackageCore();<br />
protected abstract void InvokeSimpleCore(TArgPack argPackage);<br />
}<br />
The wizard implements nearly all of the class for you. Arguments<br />
copy can be done with the DescribedArgumentsHelper static<br />
class.
Your First Application<br />
public class ListSeismicCubes :<br />
Workstep<br />
{<br />
protected override void InvokeSimpleCore(<br />
ListSeismicCubes.Arguments argPackage)<br />
{<br />
...<br />
// TODO: finish the Invoke method implementation<br />
return;<br />
}<br />
}<br />
protected override void CopyArgumentPackageCore(<br />
ListSeismicCubes.Arguments fromArgPackage,<br />
ListSeismicCubes.Arguments toArgPackage)<br />
{<br />
DescribedArgumentsHelper.Copy(fromArgPackage, toArgPackage);<br />
}<br />
...<br />
The wizard also implements two interfaces, IPresentation (for<br />
its appearance in the Process diagram) and<br />
IDescriptionSource (to display information in the Process UI).<br />
public class ListSeismicCubes : IPresentation, IDescriptionSource<br />
{<br />
#region IPresentation Members<br />
}<br />
public event EventHandler PresentationChanged;<br />
public string Text { ... }<br />
public System.Drawing.Bitmap Image { ... }<br />
#endregion<br />
#region IDescriptionSource Members<br />
public IDescription Description { ... }<br />
#endregion<br />
...<br />
ListSeismicCubes also contains an Arguments class created<br />
by the wizard, which in our case is empty since our algorithm has<br />
no arguments.<br />
public class ListSeismicCubes:Workstep<br />
...<br />
{<br />
public class Arguments : DescribedArgumentsByReflection<br />
}<br />
{<br />
}<br />
...
Your First Application<br />
Write Algorithm Code<br />
The wizard wrote nearly all of our ListSeismicCubes class<br />
except for the custom algorithm code. We will add this code in the<br />
InvokeSimpleCore method, which will be called when the<br />
workstep or process is executed.<br />
To access the APIs from the Seismic namespace, add a reference<br />
in your project to the Slb.<strong>Ocean</strong>.Petrel.DomainObject.dll.<br />
This assembly is available in the Public folder of the Petrel<br />
installation directory.<br />
Next we need to add using statements for the required<br />
namespaces. Add the following statements at the top along with the<br />
other using statements.<br />
using Slb.<strong>Ocean</strong>.Petrel.DomainObject;<br />
using Slb.<strong>Ocean</strong>.Petrel.DomainObject.Seismic;<br />
The work for the process is done in the InvokeSimpleCore<br />
method. We will add our code here:<br />
protected override void InvokeSimpleCore(<br />
ListSeismicCubes.Arguments argPackage)<br />
{<br />
if (argPackage == null)<br />
throw new ArgumentNullException("argPackage must ...");<br />
18<br />
}<br />
// TODO: finish the Invoke method implementation<br />
return;<br />
Our first step is to read the current project using the<br />
PetrelProject.PrimaryProject API.<br />
Project project = PetrelProject.PrimaryProject;<br />
The static class SeismicRoot is used to find the root of all domain<br />
objects in the seismic domain.<br />
SeismicRoot seismicRoot = SeismicRoot.Get(project);<br />
It exposes a property SeismicProject which provides navigation<br />
to all seismic collections and interpretation collections in the<br />
project.
Your First Application<br />
SeismicProject seismicProj = seismicRoot.SeismicProject;<br />
Next we parse through all the seismic collections and print the<br />
names of all seismic cubes within them using the<br />
InfoOutputWindow method of the PetrelLogger class.<br />
We build a dynamic list of surveys (SeismicCollection) to<br />
avoid recursive calls while providing a breadth traversal of the<br />
survey tree.<br />
List listCol = new List(<br />
seismicProj.SeismicCollections);<br />
for (int idx = 0; idx < listCol.Count; idx++)<br />
{<br />
SeismicCollection current = listCol[idx];<br />
foreach (SeismicColl sCol in current.SeismicCollections)<br />
{<br />
// append to the list to replace tail recursion<br />
listCol.Add(sCol);<br />
}<br />
}<br />
foreach (SeismicCube cube in current.SeismicCubes)<br />
{<br />
// Print name of Seismic cube<br />
PetrelLogger.InfoOutputWindow(current.Name +<br />
" contains Seismic Cube " + cube.Name);<br />
}<br />
The PetrelLogger static convenience class provides static<br />
methods to show a textual display of information to users. The<br />
InfoOutputWindow is used to issue an information message<br />
intended for all end users to see, but which is not highly<br />
interrupting.<br />
The complete example follows:<br />
using Slb.<strong>Ocean</strong>.Petrel.DomainObject;<br />
using Slb.<strong>Ocean</strong>.Petrel.DomainObject.Seismic;<br />
protected override void InvokeSimpleCore(<br />
ListSeismicCubes.Arguments x)<br />
{<br />
// Get current primary project<br />
Project proj = PetrelProject.PrimaryProject;<br />
// Get the root of all domain objects in the seismic domain<br />
SeismicRoot root = SeismicRoot.Get(proj);<br />
SeismicProject sProj = root.SeismicProject;<br />
// Find all seismic collections in the current project<br />
IEnumerable col = sProj.SeismicCollections;<br />
List listCol =<br />
new List(col);<br />
for(int idx = 0; idx < listCol.Count; idx++)
Your First Application<br />
}<br />
{<br />
SeismicCollection curr = listCol[idx];<br />
foreach (SeismicCollection sCol in curr.SeismicCollections)<br />
{<br />
listCol.Add(sCol);<br />
}<br />
// Find all Seismic cubes in the current collection<br />
foreach (SeismicCube cube in curr.SeismicCubes)<br />
{<br />
PetrelLogger.InfoOutputWindow(curr.Name +<br />
" contains Seismic Cube " + cube.Name);<br />
}<br />
}<br />
return;<br />
Running the Application<br />
After modifying the InvokeSimpleCore method, build the<br />
solution and start Petrel. When Petrel comes up, open the demo<br />
project deployed with the <strong>Ocean</strong> SDK. Then go to the Process<br />
diagram window and look for “ListSeismicCubes”.<br />
20
Your First Application<br />
Figure 0-2: ListSeismicCubes selected in Process diagram<br />
Double click on it to start the process and display the<br />
ListSeismicCubes dialog. The short and long descriptions are<br />
displayed in the top section of the dialog. There are no arguments<br />
for our process so the lower section is empty.
Your First Application<br />
Figure 0-3: ListSeismicCubes dialog<br />
Click Apply in the dialog to run the process; the ListSeismicCubes<br />
process will display the seismic cubes in the Petrel message log.<br />
22
Your First Application<br />
Figure 0-4: ListSeismicCubes output messages<br />
Exit Petrel. You have now written, built, and run your first <strong>Ocean</strong><br />
application.
The Petrel Data Domain<br />
<strong>Ocean</strong> Exposes Petrel’s Data<br />
Model<br />
<strong>Ocean</strong> accesses data stored in Petrel Projects. The API exposes<br />
the data objects contained in the current primary Project.<br />
Entities and Properties<br />
A Project built by Petrel contains data elements representing earth<br />
entities (a Well, a Surface, a Reservoir). These entities are<br />
characterized by Properties (such as Logs along a well bore, Height<br />
24
The Petrel Data Domain<br />
fields across a surface, Porosity distributions in a reservoir). Both<br />
types of data are exposed in <strong>Ocean</strong> by classes, called Domain<br />
Objects.<br />
The entity instance carries the geometry of the object it represents.<br />
The property object only carries property values.<br />
Property Versions<br />
Each Property object in <strong>Ocean</strong> has a Property Version attached to<br />
it, so that an application working with that Property has a way to:<br />
1 Distinguish between two properties with the same<br />
characteristics (versioning)<br />
2 Knows how to display the property values by applying the<br />
proper template (unit, scale and color table)<br />
Every Property type object in the Petrel Project has a Property<br />
Version member. When creating a Property with the API, a Property<br />
Version also has to be supplied, by extracting it from an existing<br />
object or by creating it with<br />
PetrelSystem.PropertyVersionService.<br />
IUnitMeasurement<br />
PropertyVersion<br />
ITemplate<br />
Figure 3-1 - Property Version, Unit Measurement and Template<br />
Classes<br />
To find or create a Property Version, we can refer to a Template<br />
which is known to Petrel. A list of such templates is available from<br />
the PetrelUnitSystem static class.
The Petrel Data Domain<br />
public void CreateProperty(...)<br />
{<br />
IPropertyVersionService pvs;<br />
pvs = PetrelSystem.PropertyVersionService;<br />
ITemplate templ = PetrelUnitSystem.TemplateGroupVolume.HCPVGas;<br />
IPropertyVersionContainer pvc =<br />
PetrelSystem.GetGlobalPropertyVersionContainer();<br />
PropertyVersion pv = pvs.FindOrCreate(pvc, templ);<br />
// Create Property instance with Property Version pv<br />
...<br />
return;<br />
}<br />
For more details on Property Versions (and Log Property Versions)<br />
refer to the <strong>Ocean</strong> Developer’s <strong>Guide</strong>.<br />
User View of the Petrel Data Model<br />
The Petrel Project organizes the user view of the Project in different<br />
trees:<br />
1 Input<br />
2 Models<br />
3 Results<br />
4 Cases<br />
All the trees are accessible via the API. Data is organized in<br />
pseudo-folders, seen in the API as object Collections.<br />
26
The Petrel Data Domain<br />
Input Data<br />
Input data gathers all data that is needed to build a static model.<br />
This includes all data acquired and processed (well logs, geological<br />
markers, seismic data sets, seismic attributes) and all data that is<br />
interpreted from the field measurements (petrophysical properties,<br />
seismic time interpretations, velocity functions).<br />
Most domain objects visible in the Input data tree are accessible in<br />
<strong>Ocean</strong> from the following specialized collections:<br />
1 BoreholeCollection – This collection class contains<br />
Boreholes, Logs and Completions, as well as nested<br />
BoreholeCollections. See the Well data model for details.
The Petrel Data Domain<br />
2 MarkerCollection – This is the Geology data model.<br />
These collections contain Markers, Horizons, Zones and<br />
Fluid Contacts.<br />
3 SeismicCollection – for 3D and 2D Seismic.<br />
4 InterpretationCollection – for all Seismic<br />
interpretation.<br />
5 Collection – Other folders are accessible as generic<br />
collections. From these collections, the API can access<br />
point sets, polyline sets and surfaces.<br />
Other objects in the Input data tree are inaccessible.<br />
28
The Petrel Data Domain<br />
Models<br />
The Models tree includes all elements of the Pillar grid static model<br />
and the property objects that represent Property distributions<br />
throughout the Pillar grid cell array.<br />
Domain objects visible in the Model data tree are accessible from<br />
the following collections:<br />
1 ModelCollection. This top level container in the Model<br />
tree enumerates all the Models (pillar grids) in the tree.<br />
Model collections are not nested.
The Petrel Data Domain<br />
2 Grid.PropertyCollection. Specific to each Model, the<br />
collection of all Property and FaultProperty instances in that<br />
Model.<br />
3 Fault.Properties. This lists all Fault Properties, per<br />
fault, in a pillar grid Model.<br />
Other parts of the Model tree (filters under pillar grids and Velocity<br />
models) are inaccessible.<br />
Results<br />
30<br />
Results are simulation results imported back in Petrel to compare<br />
the static models with its possible evolution through time. They are<br />
cross referenced by ResultProperty (the type of property the<br />
result lists versus time) and ResultCategory (earth model entity<br />
filter such as the Well for which that result data series is relevant).<br />
Results filters are organized in a hierarchy:
The Petrel Data Domain<br />
1 AnalysisRoot.ResultCategories lists all categories.<br />
Categories are nested. Omitting a category filter will retrieve<br />
cumulated results at the root category level.<br />
2 AnalysisRoot.ResultProperties lists all properties.<br />
ResultCategory and ResultProperty objects are required to<br />
retrieve time series results.<br />
Cases<br />
The Cases tree expose simulation cases based on different<br />
hypotheses but also represent various interpretation versions<br />
driving static model property variations. Cases contain<br />
CaseAnalysis objects (one level nesting).<br />
Simulation cases can be listed at the root level. Like other Analysis<br />
cases, Simulation cases are not nested:<br />
1 SimulationRoot.Simulations lists all cases which<br />
carry simulation data.<br />
Simulation results are referenced with the same result properties<br />
and result categories as general analysis results.
The Petrel Data Domain<br />
Data Access<br />
The <strong>Ocean</strong> API offers the following data access types:<br />
1 Read<br />
2 Update<br />
3 Create<br />
4 Delete<br />
The API reads existing data, browsing through data folders,<br />
enumerating collection contents and following the parts-parts-of<br />
relationships.<br />
The API updates existing objects, modifying entity characteristics<br />
(such as its geometry) and modifying Property values (like<br />
correcting log values in chosen points).<br />
The API creates new instances of Petrel object classes,<br />
augmenting the Project with new data the same way Petrel<br />
applications do.<br />
Last the API deletes objects that are no longer needed in the<br />
Project.<br />
32
The Petrel Data Domain<br />
Data Types Exposed<br />
The following tables show the API Read, Update, Create and Delete<br />
functionalities implemented in different parts of the Data Model.<br />
Details are given in the compiled html manual part of the <strong>Ocean</strong><br />
SDK release, <strong>Ocean</strong>.chm in the Documentation subdirectory.<br />
Seismic<br />
<strong>Ocean</strong> classes for Seismic are found in namespace<br />
Slb.<strong>Ocean</strong>.Petrel.DomainObject.Seismic<br />
Object type Read Update Create Delete<br />
3D Cube<br />
2D line<br />
Horizon<br />
Interpretation<br />
Fault Interpretation<br />
2D line creation is restricted (to cloning a 2D survey). Virtual cubes<br />
are accessed via class IAttribute. See developers manual for<br />
details.<br />
Well<br />
Namespace Slb.<strong>Ocean</strong>.Petrel.DomainObject.Well<br />
Object type Read Update Create Delete
The Petrel Data Domain<br />
Borehole/Trajectory<br />
Log<br />
Checkshot<br />
Completion<br />
Different types of Logs are available, property valued well logs,<br />
facies well logs, point logs and multitrace well logs. We cover<br />
property valued logs in this guide, you can look for more log types<br />
and greater details in the developers manual.<br />
Geology<br />
Namespace Slb.<strong>Ocean</strong>.Petrel.DomainObject.Well<br />
Object type Read Update Create Delete<br />
Marker<br />
Horizon<br />
Zone<br />
Fault<br />
Interface<br />
Stratigraphy columns may be created across a number of wells.<br />
Fault and Interface type Surfaces are just named references.<br />
34
The Petrel Data Domain<br />
Shapes<br />
Namespace Slb.<strong>Ocean</strong>.Petrel.DomainObject.Shapes<br />
Object type Read Update Create Delete<br />
Regular Height Field<br />
Surface<br />
PointSet<br />
Property<br />
PolylineSet<br />
Properties can be added to Point Sets and Surfaces, not to Polyline<br />
Sets. Surfaces can be irregular or gridded (regular height field).<br />
Both can be assigned property values.<br />
Pillar Grid<br />
Namespace Slb.<strong>Ocean</strong>.Petrel.DomainObject.PillarGrid<br />
Object type Read Update Create Delete<br />
Pillar Grid (Geometry)<br />
Property<br />
Horizon<br />
Fault, Zone, Segment<br />
Fault Property
The Petrel Data Domain<br />
In <strong>Ocean</strong> 2008 the geometry of the pillar grid is immutable, except<br />
for nudging a Horizon along a pillar. The structural elements of the<br />
grid (segments, faults, horizons and zones) cannot be changed nor<br />
removed. Properties and fault properties are fully accessible.<br />
Pillar grid property retains some knowledge of statistical treatment<br />
done at creation time, like which cells have been upscaled and<br />
statistical distributions of facies values. See the developers manual<br />
for details.<br />
Case analysis<br />
Namespace Slb.<strong>Ocean</strong>.Petrel.DomainObject.Analysis<br />
Object type Read Update Create Delete<br />
Case / Case Run<br />
Result Property<br />
Result Category<br />
Result vs Time<br />
A data analysis case has a number of case runs, each providing<br />
time series (results vs time) for combination of result categories<br />
(fields, wells) and result properties (oil production, water flow rate,<br />
etc.).<br />
36
The Petrel Data Domain<br />
Results<br />
Namespace Slb.<strong>Ocean</strong>.Petrel.DomainObject.Simulation<br />
Object type Read Update Create Delete<br />
Simulation<br />
Streamline<br />
Streamline Property<br />
Streamlines can be added to various stream line sets in a<br />
simulation case. Property values can also be read and appended<br />
and new property types added.<br />
Simulation cases inherit from general case analyses and are the<br />
source of time series which are identical to data analysis results.
The Petrel Data Domain<br />
Read Access<br />
38<br />
Browsing through collections<br />
There is no query service implemented on the Petrel Project as it is<br />
not implemented in a relational database.<br />
Instead data browsing is done by following the non-taxonomic<br />
hierarchy.<br />
At the top level we have specific collections:<br />
1 SeismicCollection, InterpretationCollection<br />
2 BoreholeCollection<br />
3 MarkerCollection<br />
4 ModelCollection, PropertyCollection<br />
5 AnalysisRoot.ResultCategories<br />
6 AnalysisRoot.ResultProperties<br />
7 Cases, Simulations<br />
Collections are nested. It is possible to browse through the whole<br />
data domain of a project by accessing the root collection of each<br />
type.<br />
From each collection, the API can access the entities and the<br />
properties that are relevant to that collection type.
The Petrel Data Domain<br />
Seismic<br />
Seismic data is rooted in the Project’s SeismicProject instance,<br />
which is a singleton, once it has been created. It is accessed, if it<br />
exists, from the SeismicRoot of the project. It only exists if there<br />
are seismic data in the project.<br />
Below the SeismicProject, we have two separate hierarchies in<br />
the Seismic domain, one for seismic data, with nested<br />
SeismicCollection objects, the other for interpretation picks<br />
with a nested hierarchy of InterpretationCollection<br />
instances.<br />
Project proj = PetrelProject.PrimaryProject;<br />
SeismicRoot sr = SeismicRoot.Get(proj);<br />
SeismicProject sp = sr.GetOrCreateSeismicProject();<br />
// navigate the Seismic data hierarchy<br />
foreach (SeismicCollection scol in sp.SeismicCollections)<br />
{<br />
...<br />
}<br />
// navigate the Seismic interpretation hierarchy<br />
foreach(InterpretationCollection icol in<br />
sp.InterpretationCollections)<br />
{<br />
...<br />
}
The Petrel Data Domain<br />
Seismic data in a SeismicCollection include either seismic<br />
cubes or seismic 2D surveys (collections of lines).<br />
Interpretation data include HorizonInterpretation or<br />
FaultInterpretation instances each including 3D and 2D time<br />
picks that can be accessed separately.<br />
Well and Geology<br />
40<br />
Wells belong to a nested hierarchy of BoreholeCollection<br />
objects that contain boreholes, trajectories and logs.<br />
Markers are stored under stratigraphic columns or horizon, zone<br />
and marker collections. At the top level, we find a list of such
The Petrel Data Domain<br />
collections, implemented in the API by the MarkerCollection<br />
class.<br />
All well and geology objects are accessible at the root of the<br />
project from the WellRoot class. WellRoot is unique for the<br />
Project and retrieved from a static method in the WellRoot class.<br />
Project proj = PetrelProject.PrimaryProject;<br />
WellRoot wr = WellRoot.Get(proj);<br />
// Get top level Borehole Collection<br />
BoreholeCollection bhcol = wr.BoreholeCollections;<br />
...<br />
Once the WellRoot instance is retrieved, it gives access to<br />
borehole and marker collections.<br />
public sealed class WellRoot : ...<br />
{<br />
public BoreholeCollection BoreholeCollection { get; }<br />
...<br />
public int WellLogVersionCount { get; }<br />
public IEnumerable WellLogVersions { get; }<br />
...<br />
public int MarkerCollectionCount { get; }<br />
public IEnumerable MarkerCollections { get; }<br />
}<br />
Boreholes are found in nested collections, rooted at the top level<br />
collection retrieved from WellRoot.<br />
Marker collections are not nested. At the top level, list of<br />
collections are found, each corresponding to a different<br />
stratigraphic classification.
The Petrel Data Domain<br />
Static Model<br />
Pillar grids are in a hierarchy that contains the property hierarchies.<br />
The hierarchy retrieval starts from the PillarGridRoot object.<br />
PillarGridRoot grid = PillarGridRoot.Get(proj);<br />
IEnumerable mcolls;<br />
mcolls = ModelCollection.GetRootModelCollections(proj);<br />
foreach (ModelCollection mcol in mcolls)<br />
{<br />
foreach (Grid g in gr.GetGrids(mcol)<br />
{<br />
...<br />
}<br />
}<br />
Each Grid object interfaces a full static model with intersecting<br />
horizons and pillar-based faults.<br />
Grid g = ...<br />
foreach (Fault f in g.Faults)<br />
{<br />
...<br />
}<br />
foreach (Horizon hor in g.Horizons)<br />
{<br />
...<br />
42<br />
}<br />
The model is populated by property values, accessed in a nested<br />
hierarchy of PropertyCollection folders.<br />
Grid g = ...
The Petrel Data Domain<br />
PropertyCollection pcol = g.PropertyCollection;<br />
foreach (Property prop in pcol.Properties)<br />
{<br />
PropertyVersion pv = prop.PropertyVersion;<br />
// recurse on subcollections<br />
foreach (PropertyCollection subcol in pcol.PropertyCollections)<br />
{<br />
...<br />
}<br />
}<br />
Simulation and Data Analysis<br />
Case analyses, result properties and result hierarchies belong to<br />
separate hierarchies. The time series are defined by combinations<br />
of these.<br />
Simulation case runs are retrieved from SimulationRoot. These<br />
simulation cases runs give access to time series and streamlines.<br />
For time series, the result property and category has to be supplied.<br />
SimulationRoot root = ...<br />
foreach (Simulation sim in root.Simulations)<br />
{<br />
StreamlineSet sls = sim.StreamlineSet;<br />
if (sls != StreamlineSet.NullObject)<br />
{<br />
foreach (StreamlineSubset sub in sls.StreamlineSubsets)<br />
{<br />
foreach (Streamline sl in sub.Streamlines)
The Petrel Data Domain<br />
}<br />
}<br />
}<br />
{<br />
}<br />
...<br />
Result properties and categories are retrieved from<br />
AnalysisRoot. With a combination of property and category a<br />
simulation case run will return the relevant time series.<br />
AnalysisRoot aroot = ...<br />
List cats;<br />
cats = new List(aroot.ResultCategories);<br />
List props;<br />
props = new List(aroot.ResultProperties);<br />
int pi = ...<br />
int ci = ...<br />
foreach (TimeSeries ts in sim.GetTimeSeries(props[pi], cats[ci]))<br />
{<br />
...<br />
}<br />
Data Updates<br />
Data update is done by modifying the object that is seen through<br />
the API. Updating an entity or a property object alters the Project.<br />
After the project has been updated, it is the user who decides<br />
whether to save the Project changes or not. But from the API<br />
standpoint, the Project has been modified as soon as an object has<br />
been edited.<br />
Any such update has to be done inside a transaction and any<br />
modified object has to be locked beforehand inside that<br />
transaction.<br />
44
The Petrel Data Domain<br />
Transactions<br />
A transaction represents a group of edits on some data (domain<br />
objects). Transactions are required for operations on native Petrel<br />
domain objects that will change the data: create, update, and<br />
delete.<br />
Transactions use the Slb.<strong>Ocean</strong>.Core.ITransaction<br />
interface:<br />
public interface ITransaction : IDisposable<br />
{<br />
void Commit();<br />
void Lock(params object[ ] objects);<br />
void LockCollection(IEnumerable objectCollection);<br />
...<br />
}<br />
To use a transaction:<br />
1. Create a transaction<br />
2. Lock domain objects that are going to be changed<br />
3. Modify the domain objects<br />
4. Commit the transaction<br />
5. Dispose of the created transaction<br />
In Petrel, transactions are used to batch event notifications. Petrel<br />
does not use a data base, so changes to domain objects made<br />
within a transaction happen immediately.<br />
Modifying Domain Objects<br />
Domain objects can be modified in the transaction after having<br />
been locked. The on-line documentation will indicate which object<br />
property can be modified.
The Petrel Data Domain<br />
The nature of the class members is also explicitly shown in the<br />
class definitions that are accessible from the Visual Studio<br />
environment.<br />
namespace Slb.<strong>Ocean</strong>.Petrel.DomainObject.Well<br />
{<br />
public sealed class WellLog : ...<br />
{<br />
public Borehole Borehole { get; }<br />
public string Comments { get; set; }<br />
...<br />
public int SampleCount { get; }<br />
public IEnumerable Samples { get; set; }<br />
...<br />
}<br />
}<br />
The array of log values, Samples, is read-write but SampleCount<br />
is read-only (dynamically evaluated from the array size).<br />
Domain Object Relationships<br />
46<br />
Object to object relationships can also be accessed via the API. For<br />
these relationships to be modifiable, the corresponding class<br />
property must allow write access, which is, as a general rule, not<br />
the case as parts-of relationships impose constraints between<br />
parent and child. For instance a pillar grid Property cannot be<br />
separated from the Grid object that defines its geometry.<br />
Even when the user can modify the containment hierarchy<br />
interactively, the API often makes the corresponding relationship<br />
immutable.<br />
However there are cases where it makes sense to allow write<br />
access to parts-of relations. This is the case when object creation<br />
does not require such a relationship to be established. For instance,<br />
it is possible to update the reference from a Marker to a given<br />
Surface (Fault or Horizon).<br />
In the example below we clear the stratigraphic references of a set<br />
of markers in a given borehole.
The Petrel Data Domain<br />
MarkerCollection mcol = ...<br />
Borehole bh = ...<br />
Horizon hor = Horizon.NullObject;<br />
foreach (Marker m in mcol.GetMarkers(bh))<br />
{<br />
m.Surface = hor;<br />
}<br />
Please refer to the on-line help to check whether the relationship is<br />
read-only.
The Petrel Data Domain<br />
Domain Object Creation<br />
Creating New Instances<br />
48<br />
Most objects accessible via the API can be instantiated.<br />
However, the API never allows class instantiation in the classical<br />
manner. API classes are sealed, and some objects are even
The Petrel Data Domain<br />
exposed simply as interfaces. To create a new class instance, the<br />
API proposes specialized Create methods.<br />
Using Transactions<br />
All domain object creations have to be done inside transactions,<br />
just like object updates. But an object creation modifies the<br />
container that this new object will be part of. It modifies its parent<br />
entity (in the non-taxonomic hierarchy) or the collection of similar<br />
entities that will harbor it. That parent or collection has to be locked<br />
in the transaction.<br />
The newly created object can be modified until the transaction<br />
commits. It is locked by default, only its parent had to be locked<br />
explicitly in the transaction.<br />
Locating the Correct Create Method<br />
The Create method is always located in the class of the containing<br />
object. Borehole contains WellLog collections, that is where the<br />
Create method for logs can be found.<br />
using (ITransaction t = DataManager.NewTransaction())<br />
{<br />
PropertyVersion pv = ...<br />
Borehole bh = ...<br />
t.Lock(bh);<br />
WellLog wl = bh.Logs.CreateWellLog(pv);<br />
...<br />
}<br />
Creating New Collections<br />
When it does not make sense to create an entity in a predefined<br />
collection, one has to create a new collection. Collections are often<br />
nested, so adding a collection is adding a branch to a tree. Classes<br />
that model nested collections allow creation of sub-collections. For
The Petrel Data Domain<br />
example a PropertyCollection can contain sub-collections, and<br />
carries a Create method for branching out at that point.<br />
ITransaction t = ...<br />
PropertyCollection pcol = ...<br />
t.Lock(pcol);<br />
string colName = "New Sub-Collection";<br />
PropertyCollection subcol = pcol.CreatePropertyCollection(colName);<br />
New top-level collections<br />
For top-level objects (like collections) the Create methods are in<br />
static classes. For example, the top level collection for Seismic data<br />
is created in the SeismicProject instance.<br />
There are few collections that can appear at the top level of Petrel<br />
data trees. Nested collections are rooted under a top level<br />
collection that is only created once. Un-nested collections, that can<br />
be added at the root of Input or Models data trees are:<br />
1 MarkerCollection<br />
2 ModelCollection<br />
3 Collection<br />
These collections are created directly from the Petrel primary<br />
Project or the WellRoot object.<br />
ITransaction t = ...<br />
Project proj = PetrelProject.PrimaryProject;<br />
WellRoot root = WellRoot.Get(proj);<br />
t.Lock(proj);<br />
proj.CreateModelCollection("New Pillar Model");<br />
t.Lock(root);<br />
root.CreateMarkerCollection("New Stratigraphic Model");<br />
Nested Collections<br />
Collections that are always grouped under a root collection are<br />
created using a specific container, WellRoot for boreholes and<br />
50
The Petrel Data Domain<br />
SeismicProject for seismic data and interpretation. These are<br />
used for nested collections.<br />
1 SeismicCollection<br />
2 InterpretationCollection<br />
3 BoreholeCollection<br />
There is no specific object at the root of all borehole collections,<br />
instead we have a generic BoreholeCollection called “Wells”<br />
that can only be created once.<br />
ITransaction t = ...<br />
Project proj = PetrelProject.PrimaryProject;<br />
SeismicRoot root = SeismicRoot.Get(proj);<br />
SeismicProject sproj = root.SeismicProject;<br />
WellRoot wroot = WellRoot.Get(proj);<br />
t.Lock(sproj);<br />
string colName = "New Collection";<br />
SeismicCollection scol = sproj.CreateSeismicCollection(colName);<br />
String iName = “New Interpretation”);<br />
InterpretationCollection icol;<br />
icol = sproj.CreateInterpretationCollection(iName);<br />
...<br />
t.Lock(wroot);<br />
BoreholeCollection bcol = wroot. GetOrCreateBoreholeCollection();<br />
...<br />
We can also create collections that are placed under an object<br />
container, like under the PillarGrid.<br />
1 PropertyCollection<br />
ITransaction t = ...<br />
Grid g = ...<br />
t.Lock(g);<br />
PropertyCollection root = g.PropertyCollection;<br />
String colName = “New Properties”;<br />
Property>Collection sub = root.CreatePropertyCollection(colName);<br />
...
The Petrel Data Domain<br />
Deletions<br />
Deletion of an existing instance is done by calling the Delete<br />
method on the object itself.<br />
Deletion is always done inside a Transaction and the object to be<br />
deleted has to be locked first.<br />
Data Access Example<br />
In the following example we will browse through wells in the<br />
project, retrieve a porosity log, browse through static models and<br />
create a new Property for the latest modified model in the<br />
project.<br />
The created property will be filled with values extracted from the<br />
well log in cells where actual log measurements exist.<br />
52
The Petrel Data Domain<br />
Browsing through well logs to find a Porosity measurement<br />
Project proj = PetrelProject.PrimaryProject;<br />
WellRoot root = WellRoot.Get(proj);<br />
BoreholeCollection wells = root.BoreholeCollection;<br />
WellLog poro = WellLog.NullObject;<br />
ITemplate temp =<br />
PetrelUnitSystem.TemplateGroupPetrophysical.Porosity;<br />
IUnitMeasurement um = temp.UnitMeasurement;<br />
foreach (BoreholeCollection bhc in wells.BoreholeCollections)<br />
{<br />
foreach (Borehole bh in bhc)<br />
{<br />
foreach (WellLog l in bh.Logs.WellLogs)<br />
{<br />
if (l.WellLogVersion.UnitMeasurement.Equals(um))<br />
{<br />
poro = l;<br />
break;<br />
}<br />
}<br />
if (!poro.IsGood) break;<br />
}<br />
if (!poro.IsGood) break;<br />
}<br />
if (poro == WellLog.NullObject) return;<br />
PetrelLogger.InfoOutputWindow("Found log " + poro.Name);<br />
Retrieving the latest model<br />
Grid latest = Grid.NullObject;<br />
DateTime last = new DateTime(1980, 1, 1);<br />
PillarGridRoot proot = PillarGridRoot.Get(proj);
The Petrel Data Domain<br />
foreach (Grid g in proot.Grids)<br />
{<br />
if (g.LastModified.Time.CompareTo(last) >= 0)<br />
{<br />
latest = g;<br />
last = latest.LastModified.Time;<br />
}<br />
}<br />
if (latest == Grid.NullObject) return;<br />
PetrelLogger.InfoOutputWindow("Found grid " + latest.Name);<br />
Creating a new property collection in the model<br />
ITransaction t = DataManager.NewTransaction();<br />
PropertyCollection pcol = latest.PropertyCollection;<br />
t.Lock(pcol);<br />
string pcolName = “New Collection”;<br />
PropertyCollection newcol =<br />
pcol.CreatePropertyCollection(pcolName);<br />
Creating the pillar grid property<br />
IPropertyVersionService pvs = PetrelSystem.PropertyVersionService;<br />
PropertyVersion gpv = pvs.FindOrCreate(latest, um);<br />
t.Lock(newcol);<br />
Property prop = newcol.CreateProperty(gpv);<br />
Filling values<br />
IPillarGridIntersectionService ipgs;<br />
ipgs = CoreSystem.GetService();<br />
IPolyline3 traj = poro.Borehole.Trajectory.Polyline;<br />
IEnumerable cells;<br />
cells = ipgs.GetPillarGridPolylineIntersections(latest, traj);<br />
foreach (SegmentCellIntersection cell in cells)<br />
{<br />
Index3 ijk = cell.EnteringCell;<br />
Point3 xyz = cell.IntersectionPoint;<br />
double index = poro.Borehole.Transform(Domain.ELEVATION_DEPTH,<br />
xyz.Z, Domain.INDEX);<br />
WellLogSample[] sam = new WellLogSample[](poro.Samples);<br />
latest[ijk] = sam[(int)depth];<br />
}<br />
t.Commit();<br />
t.Dispose();<br />
54
The Petrel Data Domain<br />
Online Manual for Data Domain<br />
<strong>Ocean</strong> comes with an online manual exposing all the classes,<br />
methods and class members implemented in the API.<br />
Opening the manual<br />
In Visual Studio 2005, the Help menu includes <strong>Ocean</strong> help. Just<br />
select Contents.
The Petrel Data Domain<br />
Using Intellisense<br />
In Visual Studio, the <strong>Ocean</strong> library references (files with “.dll”<br />
extensions) let the environment select class members and methods<br />
from a list dynamically generated from the types of the variables in<br />
the code.<br />
56
The Petrel Data Domain<br />
This helps in selecting the correct class syntax without having to<br />
explicitly search for information in the on-line manual.<br />
Class Definitions<br />
It is possible, while editing code, to go directly to the class<br />
definition of the current variable declaration. Simply right-click on<br />
the class name and Visual Studio opens the class reference.
The Petrel Data Domain<br />
Other class definitions can be then explored in cascade.<br />
58
The Petrel Data Domain
Chapter 2<br />
Extending the Petrel UI<br />
<strong>Ocean</strong> allows application developers to customize and extend the<br />
Petrel application UI. In this chapter, we will explore a very simple<br />
customization: adding your own menu item to an existing Petrel<br />
menu.<br />
Please see the <strong>Ocean</strong> Developers <strong>Guide</strong> for details on using <strong>Ocean</strong><br />
to extend the Petrel UI in this and other ways.<br />
How to Add a New Menu Item<br />
These are the steps to create a new menu item:<br />
1. Edit your module’s IntegratePresentation method.<br />
2. Use the <strong>Ocean</strong> convenience class ToolbarItem to create<br />
the menu item.<br />
3. Define the menu item properties.<br />
4. Add the menu item to the Petrel UI using the <strong>Ocean</strong> class<br />
ToolsManager.<br />
60
Adding a New Menu Item<br />
Edit IntegratePresentation method<br />
Add your new menu item in the module’s<br />
IntegratePresentation method.<br />
public void IntegratePresentation ()<br />
{<br />
// add your new menu item here<br />
}<br />
Creating UI Tools<br />
A tool is an item that appears on menus and toolbars. When a tool<br />
appears on a menu as a menu item, it is drawn as a list entry with<br />
text and an optional image.<br />
tool in menu, no image<br />
tool in toolbar<br />
Figure 3-1 - Tools in Petrel UI<br />
<strong>Ocean</strong> provides the ToolbarItem convenience class to create<br />
tools such as a menu item.<br />
public class ToolbarItem : IToolbarItem, ...<br />
{<br />
public ToolbarItem();<br />
Create an instance of the ToolbarItem convenience class:<br />
ToolbarItem myListSeismic = new ToolbarItem();
Adding a New Menu Item<br />
Define the Menu Item Properties<br />
You control the basic properties of the new menu item. Text and UI<br />
location must be specified.<br />
public class ToolbarItem : IToolbarItem, ...<br />
{<br />
public string ParentKey { set; get; }<br />
public string Text { set; get; }<br />
You can also optionally provide an image and an event handler for a<br />
menu click.<br />
}<br />
public System.Drawing.Bitmap Image { set; get; }<br />
public event System.EventHandler Click;<br />
...<br />
Text<br />
Supply the text to be shown in the menu item:<br />
myListSeismic.Text = "List Seismic Cubes";<br />
UI Location<br />
To specify the UI location of the new menu item, you need to give<br />
the parent key. In this case, the parent is the existing Petrel menu<br />
that contains the new menu item.<br />
Figure 3-2 – Standard Petrel Menus<br />
62<br />
The <strong>Ocean</strong> WellKnownUI.Menus static convenience class has a<br />
public static string field for each existing Petrel menu.<br />
WellKnownUI.Menus.File<br />
WellKnownUI.Menus.Edit<br />
WellKnownUI.Menus.View<br />
WellKnownUI.Menus.Insert<br />
WellKnownUI.Menus.Project<br />
WellKnownUI.Menus.Tools<br />
WellKnownUI.Menus.Window
Adding a New Menu Item<br />
WellKnownUI.Menus.Help<br />
WellKnownUI.Menus.MainMenu<br />
Figure 3-3 - WellKnownUI.Menus Static Read-only String Properties<br />
We want our new menu item to go in the Tools menu, so we specify<br />
the Tools field:<br />
myListSeismic.ParentKey = WellKnownUI.Menus.Tools;<br />
Image<br />
Optionally set the image for the menu item. You can use an image<br />
from Petrel available from the static class PetrelImages, or<br />
provide your own.<br />
myListSeismic.Image = PetrelImages.Seismic3D;<br />
Click Event Handler<br />
Finally we will specify what is going to happen when the menu item<br />
is clicked. Our menu item will run the same algorithm we wrote for<br />
our workstep.<br />
We need to add a click handler to our menu item instance:<br />
myListSeismic.Click += myListSeismicToolClick;<br />
The click event handler is a System.EventHandler and looks<br />
like:<br />
using Slb.<strong>Ocean</strong>.Petrel.DomainObject;<br />
using Slb.<strong>Ocean</strong>.Petrel.DomainObject.Seismic;<br />
private void myListSeismicToolClick(object sender, EventArgs e)<br />
{<br />
Project project = PetrelProject.PrimaryProject;<br />
SeismicRoot seismicRoot = SeismicRoot.Get(project);<br />
SeismicProject seismicProj = seismicRoot.SeismicProject;<br />
foreach(SeismicCollection scol in<br />
seismicProj.SeismicCollections)<br />
{<br />
if (scol.SeismicCubeCount > 0)<br />
{<br />
foreach (SeismicCube cube in scol.SeismicCubes)<br />
{<br />
PetrelLogger.InfoOutputWindow(scol.Name +<br />
" contains Seismic Cube " +
Adding a New Menu Item<br />
}<br />
}<br />
}<br />
}<br />
cube.Name);<br />
Add to ToolsManager<br />
At this point, we have defined the new menu item (text, image,<br />
location and what will happen when the tool is clicked), but it is not<br />
in the Petrel user interface yet.<br />
The ToolsManager class provides functionality to add menu<br />
items. Use the Add method on ToolsManager to add the new<br />
menu item into the Petrel UI.<br />
public sealed class ToolsManager<br />
{<br />
public void Add (IToolbarItem item);<br />
...<br />
}<br />
We can access this <strong>Ocean</strong> service from a read only property of the<br />
convenience class PetrelSystem.Tools. We will add our new<br />
tool to the UI after the tool is created:<br />
PetrelSystem.Tools.Add(myListSeismic);<br />
Results<br />
Putting everything together, the example to add a new menu item<br />
into the Petrel Tools menu is as follows:<br />
public void IntegratePresentation ()<br />
{<br />
ToolbarItem myListSeismic = new ToolbarItem();<br />
64
Adding a New Menu Item<br />
}<br />
myListSeismic.ParentKey = WellKnownUI.Menus.Tools;<br />
myListSeismic.Text = "List Seismic Cubes";<br />
myListSeismic.Image = PetrelImages.Seismic3D;<br />
myListSeismic.Click += myListSeismicToolClick;<br />
PetrelSystem.Tools.Add(myListSeismic);<br />
void myListSeismicToolClick(object sender, EventArgs e)<br />
{<br />
Project project = PetrelProject.PrimaryProject;<br />
SeismicRoot seismicRoot = SeismicRoot.Get(project);<br />
SeismicProject seismicProj = seismicRoot.SeismicProject;<br />
}<br />
foreach(SeismicCollection scol in<br />
seismicProj.SeismicCollections)<br />
{<br />
if (scol.SeismicCubeCount > 0)<br />
{<br />
foreach (SeismicCube cube in scol.SeismicCubes)<br />
}<br />
}<br />
{<br />
}<br />
PetrelLogger.InfoOutputWindow(scol.Name +<br />
" contains Seismic Cube " +<br />
cube.Name);<br />
Now, after you rebuild your module and run Petrel, you will see a<br />
new menu item called “List Seismic Cubes” in the Petrel Tools<br />
menu:<br />
Figure 3-4 - New “List Seismic Cubes” Menu Item in Petrel Tools<br />
Menu
Chapter 3<br />
Extending the Data Domain<br />
<strong>Ocean</strong> allows you to customize and extend the Petrel application by<br />
creating new data objects, called custom domain objects. Custom<br />
domain objects can contribute implementations for, and participate<br />
in, standard Petrel behavior. They can be:<br />
• Added to the Input and Model trees<br />
• Displayed in a standard Petrel window<br />
You can decide what functionality to implement for a given custom<br />
domain object by deciding what interfaces (services) to implement<br />
and register with <strong>Ocean</strong>. All are optional in terms of <strong>Ocean</strong><br />
requirements.<br />
This chapter will describe how to write a simple custom domain<br />
object and have it participate in standard Petrel behavior.<br />
For details on these topics and more, please see the <strong>Ocean</strong><br />
Developers <strong>Guide</strong>.<br />
66
Extending the Data Domain<br />
Basic Custom Domain Object<br />
A custom domain object in Petrel is just a new class. The most<br />
basic custom domain object is a class that inherits from the .NET<br />
object, even when it doesn't implement any <strong>Ocean</strong> interfaces.<br />
Consider a simple object that has a position in space and a radius<br />
that defines its size.<br />
public class XYZObj<br />
{<br />
private float m_x = 100.0f;<br />
private float m_y = 100.0f;<br />
private float m_z = 100.0f;<br />
private float m_radius = 100.0f;<br />
}<br />
public float X<br />
{<br />
get { return m_x; }<br />
set { m_x = value; }<br />
}<br />
public float Y<br />
{<br />
get { return m_y; }<br />
set { m_y = value; }<br />
}<br />
public float Z<br />
{<br />
get { return m_z; }<br />
set { m_z = value; }<br />
}<br />
public float Radius<br />
{<br />
get { return m_radius; }<br />
set { m_radius = value; }<br />
}
Extending the Data Domain<br />
This basic object is a valid custom domain object that can<br />
participate in Petrel behavior. It can be added to the Petrel Input<br />
and Model trees.<br />
Adding to Input and Models Trees<br />
The Input tree and the Models tree support the addition of new<br />
nodes (domain objects) using the IInput and IModels interfaces.<br />
public interface IInput : ...<br />
{<br />
void Add(object item );<br />
}<br />
public interface IModels : ...<br />
{<br />
void Add(object item );<br />
}<br />
You can access these interfaces via the PetrelProject class.<br />
using Slb.<strong>Ocean</strong>.Petrel;<br />
using Slb.<strong>Ocean</strong>.Petrel.UI;<br />
PetrelProject.Inputs.Add( new XYZObj( ) );<br />
PetrelProject.Models.Add( new XYZObj( ) );<br />
Figure 4-1 – XYZ object in Input and Models trees.<br />
68
Extending the Data Domain<br />
Adding to Native Petrel Domain<br />
Objects<br />
Custom domain objects may be added as children to some native<br />
Petrel domain objects using the IExtensions interface:<br />
public interface IExtensions : IEnumerable, IEnumerable<br />
{<br />
void Add( object item );<br />
bool Remove( object item );<br />
bool Contains( object item );<br />
...<br />
}<br />
This example adds a XYZObj custom domain object to a<br />
Borehole, a native Petrel domain object. Start with a Borehole<br />
object.<br />
Slb.<strong>Ocean</strong>.Petrel.DomainObject.Well.Borehole bore = ...;<br />
You must use a transaction when adding the children. The<br />
transaction is created by the DataManager API.<br />
using (ITransaction txn = DataManager.NewTransaction())<br />
We lock the Borehole since we are going to change its children.<br />
txn.Lock(bore);<br />
Now we add a new XYZObj using IExtensions.<br />
bore.Extensions.Add(new XYZObj());<br />
Finally the operation is completed by committing the transaction.<br />
txn.Commit();<br />
Putting it all together we have:<br />
Slb.<strong>Ocean</strong>.Petrel.DomainObject.Well.Borehole bore = ...;<br />
using (ITransaction txn = DataManager.NewTransaction())<br />
{<br />
txn.Lock(bore);<br />
bore.Extensions.Add(new XYZObj());<br />
txn.Commit();<br />
}
Extending the Data Domain<br />
70<br />
Figure 4-2 – XYZObj added to a Borehole object (A10).<br />
Adding an Object from a Context<br />
Menu<br />
All native Petrel objects have context menus that appear with a<br />
right mouse button click on the object in the tree. We will add our<br />
own context menu item to the Borehole context menu which will<br />
add a XYZObj as a child. Define a context menu item using the<br />
ContextMenuItem class:<br />
public class ContextMenuItem : IContextMenuItem,<br />
IPresentation where TDomainObject : class<br />
{<br />
public ContextMenuItem ( );<br />
public ContextMenuItem ( string text,<br />
ContextMenuEventHandler clickCallback );<br />
public string Text { get; set; }<br />
public event ContextMenuEventHandler Click;<br />
...<br />
}
Extending the Data Domain<br />
Add the new context menu item in the module’s<br />
IntegratePresentation method. First we create a Borehole<br />
context menu item, and then add it to the Petrel UI using the<br />
PetrelSystem.Tools service.<br />
ContextMenuItem cItem = new ContextMenuItem(<br />
“Insert XYZ object”, AddXYZClick );<br />
PetrelSystem.Tools.Add( cItem );<br />
An event handler must be provided to respond to the click of the<br />
context menu item. The AddXYZClick event handler will add the<br />
XYZObj object to the Borehole from the context menu.<br />
public void AddXYZClick( object sender,<br />
ContextMenuEventArgs e )<br />
{<br />
Borehole bore = e.ContextObject as Borehole;<br />
Completing our example, the event handler is now:<br />
public void AddXYZClick( object sender,<br />
ContextMenuEventArgs e )<br />
{<br />
// Get the borehole object<br />
Borehole bore = e.ContextObject as Borehole;<br />
if (bore == null)<br />
return;<br />
}<br />
using (ITransaction txn = DataManager.NewTransaction())<br />
{<br />
txn.Lock(bore);<br />
bore.Extensions.Add(new XYZObj());<br />
txn.Commit();<br />
}<br />
The new “Insert XYZ object” context menu item is added to the<br />
bottom of the Borehole context menu.
Extending the Data Domain<br />
Figure 4-3 – Context menu containing XYZ object.<br />
Customizing Tree Presentation<br />
A new custom domain object has a default presentation style when<br />
displayed in the tree. The icon is the <strong>Ocean</strong> bubbles icon, and the<br />
text is object.ToString.<br />
Figure 4-4 - Default Presentation in Tree<br />
72<br />
An object can control its appearance in the Petrel trees by<br />
implementing the Slb.<strong>Ocean</strong>.Petrel.UI.IPresentation<br />
interface.
Extending the Data Domain<br />
public interface IPresentation<br />
{<br />
public Bitmap Image { get; }<br />
public string Text { get; }<br />
public event EventHandler PresentationChanged;<br />
}<br />
IPresentation allows the custom domain object to provide a<br />
bitmap and a text string to be displayed in the user interface. It also<br />
provides an event that is raised when the presentation has<br />
changed.<br />
Implementing IPresentation for our XYZObj object gives us:<br />
using Slb.<strong>Ocean</strong>.Petrel.UI;<br />
public class XYZObj : IPresentation<br />
{<br />
public System.Drawing.Bitmap Image<br />
{<br />
get { return MyImageLibrary.XYZObjImage; }<br />
}<br />
}<br />
}<br />
public string Text<br />
{<br />
get { return “XYZ Object”; }<br />
}<br />
public event EventHandler PresentationChanged { add {} remove {}<br />
...<br />
In this case the object, MyImageLibrary, contains a resource for<br />
the bitmap displayed with the object name.<br />
Figure 4-5 - Custom Presentation of XYZ object
Extending the Data Domain<br />
Object Visualization<br />
A custom domain object may be rendered in standard Petrel<br />
windows. The 3D and 2D windows render data using OpenInventor<br />
(Oiv), while the Map and WellSection windows use .NET GDI+ to<br />
perform the rendering.<br />
3D Window Display<br />
Two steps must be performed to render a custom object in the 3D<br />
or 2D window.<br />
5. Implement an OpenInventor factory for the object type<br />
6. Add the factory service to <strong>Ocean</strong><br />
We implement the IOpenInventorFactory interface to provide<br />
the OpenInventor factory for displaying our object.<br />
public interface IOpenInventorFactory<br />
{<br />
bool CanCreate(object domainObj, OpenInventorContext c);<br />
SoNode Create(object domainObj, OpenInventorContext c);<br />
void Dispose(SoNode node, object dObj, OpenInventorContext c);<br />
void Update( SoNode node, object dObj, OpenInventorContext c);<br />
}<br />
We start by creating a class to implement<br />
IOpenInventorFactory.<br />
public class MyXYZObjOivFactory : IOpenInventorFactory<br />
74
Extending the Data Domain<br />
Next we implement the IOpenInventorFactory methods.<br />
CanCreate determines if the domain object can be rendered in the<br />
window. It can use properties of the object or the context to decide.<br />
public bool CanCreate( object o, OpenInventorContext ctx )<br />
{<br />
return true;<br />
}<br />
Create is called the first time an OpenInventor scenegraph is<br />
needed to display an object. It creates a new root node for the<br />
OpenInventor node structure and then calls the Update method to<br />
handle the display.<br />
public SoNode Create( object o, OpenInventorContext ctx )<br />
{<br />
SoSeparator root = new SoSeparator( );<br />
Update( root, o, ctx );<br />
return root;<br />
}<br />
The Update method actually builds the node hierarchy for the<br />
object being displayed. The position of the XYZObj object is<br />
translated into the window coordinates. A sphere is defined to<br />
represent it, and is added to the node hierarchy. Refer to<br />
OpenInventor documentation for details.<br />
public void Update( SoNode node, object o, OpeninventorContext ctx)<br />
{<br />
SoSeparator root = node as SoSeparator;<br />
root.RemoveAllChildren( );<br />
}<br />
// Set up the translation for our object<br />
XYZObj xyz = (XYZObj) o;<br />
SbVec3f v3 = ctx.WorldToWindow3D(xyz.X, xyz.Y, xyz.Z);<br />
SoTranslation trans = new SoTranslation();<br />
trans.translation.SetValue(v3);<br />
root.AddChild(trans);<br />
// Create a sphere with our objects radius<br />
SoSphere s = new SoSphere();<br />
s.radius.SetValue( xyz.Radius );<br />
root.AddChild(s);<br />
return;<br />
Dispose should clean up any resources associated with the<br />
scenegraph, but not the scenegraph itself.
Extending the Data Domain<br />
public void Dispose( SoNode n, object o, OpenInventorContext ctx )<br />
{<br />
}<br />
The complete OpenInventor factory class for rendering the XYZObj<br />
object is:<br />
using MC.Inventor;<br />
using MC.Inventor.Nodes;<br />
using Slb.<strong>Ocean</strong>.Petrel.DomainObject;<br />
using Slb.<strong>Ocean</strong>.Petrel.UI;<br />
public class MyXYZObjOivFactory : IOpenInventorFactory<br />
{<br />
public bool CanCreate( object o, OpenInventorContext ctx )<br />
{<br />
return true;<br />
}<br />
}<br />
public SoNode Create( object o, OpenInventorContext ctx )<br />
{<br />
SoSeparator root = new SoSeparator( );<br />
Update( root, o, ctx );<br />
return root;<br />
}<br />
public void Update(SoNode n, object o, OpeninventorContext ctx)<br />
{<br />
SoSeparator root = n as SoSeparator;<br />
root.RemoveAllChildren( );<br />
// Set up the translation<br />
XYZObj xyz = (XYZObj) o;<br />
SbVec3f v3 = ctx.WorldToWindow3D(xyz.X, xyz.Y, xyz.Z);<br />
SoTranslation trans = new SoTranslation( );<br />
trans.translation.SetValue( v3 );<br />
root.AddChild( trans );<br />
// Create a sphere using objects radius<br />
SoSphere s = new SoSphere( );<br />
s.radius.SetValue( xyz.Radius );<br />
root.AddChild( s );<br />
return;<br />
}<br />
public void Dispose(SoNode n, object o, OpenInventorContext ctx )<br />
{<br />
}<br />
The OpenInventor factory must be added as a service in the<br />
module’s Initialize method to display the XYZObj object.<br />
public void Initialize ( )<br />
{<br />
Type objType = typeof( XYZObj );<br />
Type factoryType = typeof( IOpenInventorFactory );<br />
XYZObjOivFactory factory = new XYZObjOivFactory( );<br />
CoreSystem.Services.AddService( objType, factoryType, factory);<br />
}<br />
76
Extending the Data Domain<br />
When a 3D window is active, the XYZObject object will have a<br />
checkbox next to it indicating that it may be displayed.<br />
Figure 4-6 – XYZ Object ready to display in 3D window.<br />
When the end user checks the checkbox the object will be<br />
displayed.<br />
Figure 4-7 – XYZObj object in 3D window<br />
Map Window Display<br />
Two steps are required to render the custom domain object in the<br />
Map window.<br />
Implement the IMapWindow factory interface.<br />
Add the factory to <strong>Ocean</strong> as a service.<br />
We implement the IMapRenderer interface to display the<br />
XYZObj.<br />
public interface IMapRenderer<br />
{<br />
void Initialize(object domainObj, MapRendererContext ctx);<br />
bool CanDraw (object domainObj, MapRendererContext ctx);<br />
void Draw (object domainObj, MapRendererContext ctx);<br />
Box2 GetBounds (object domainObj, MapRendererContext ctx);
Extending the Data Domain<br />
}<br />
void Dispose<br />
(object domainObj, MapRendererContext ctx);<br />
We start by defining the class that implements the IMapRenderer<br />
interface. We will render our object as a filled circle.<br />
public class XYZObjMapDisplay : IMapRenderer<br />
Next we implement the Initialize method. Our simple XYZObj<br />
has nothing to do.<br />
public void Initialize ( object o, MapRendererContext ctx )<br />
{<br />
return;<br />
}<br />
The CanDraw method reports whether or not the renderer knows<br />
how to draw the object. In our case it will always return true.<br />
public bool CanDraw ( object o, MapRendererContext ctx )<br />
{<br />
return true;<br />
}<br />
Draw renders a representation of the object into the window using<br />
the appropriate GDI+ drawing surfaces. It gets the world<br />
coordinates from the context for drawing. It defines the brush style<br />
and color and uses the properties of the XYZObj instance to draw<br />
a filled ellipse.<br />
public void Draw ( object o, MapRendererContext ctx )<br />
{<br />
// Get the World coordinates<br />
Graphics gworld = ctx.World;<br />
// Define the brush in blue<br />
using (Brush br = new SolidBrush(Color.Blue))<br />
{<br />
XYZObj xyz = (XYZObj)o;<br />
int x = (int)xyz.X;<br />
int y = (int)xyz.Y;<br />
int size = (int)xyz.Radius;<br />
78<br />
}<br />
}<br />
// Draw the circle<br />
gworld.FillEllipse(br, x-size, y-size, size*2, size*2);<br />
GetBounds computes the bounding box that the instance occupies<br />
and returns a Box2.<br />
public Box2 GetBounds ( object o, MapRendererContext ctx )<br />
{<br />
XYZObj xyz = (XYZObj)o;<br />
double x = xyz.X;
Extending the Data Domain<br />
}<br />
double y = xyz.Y;<br />
double size = xyz.Radius;<br />
Point2 begin = new Point2( x - size, y - size );<br />
Point2 end = new Point2( x + size, y + size );<br />
return new Box2( begin, end );<br />
Dispose has nothing to do for our simple XYZObj object.<br />
public void Dispose ( object o, MapRendererContext ctx )<br />
{<br />
return;<br />
}<br />
The complete IMapRenderer implementation for XYZObj is:<br />
using System.Drawing;<br />
using System.Drawing.Drawing2D;<br />
using Slb.<strong>Ocean</strong>.Geometry;<br />
using Slb.<strong>Ocean</strong>.Petrel.UI;<br />
public class XYZObjMapDisplay : IMapRenderer<br />
{<br />
public void Initialize ( object o, MapRendererContext ctx )<br />
{<br />
return;<br />
}<br />
public bool CanDraw ( object o, MapRendererContext ctx )<br />
{<br />
return true;<br />
}<br />
public void Draw ( object o, MapRendererContext ctx )<br />
{<br />
// Get the World coordinates<br />
Graphics gworld = ctx.World;<br />
using (Brush br = new SolidBrush(Color.Blue))<br />
{<br />
XYZObj xyz = (XYZObj)o;<br />
int x = (int)xyz.X;<br />
int y = (int)xyz.Y;<br />
int size = (int)xyz.Radius;<br />
}<br />
}<br />
// Draw the circle<br />
gworld.FillEllipse(br, x-size, y-size, size*2, size*2);<br />
public Box2 GetBounds ( object o, MapRendererContext ctx )<br />
{<br />
XYZObj xyz = (XYZObj)o;<br />
double x = xyz.X;<br />
double y = xyz.Y;<br />
double size = xyz.Radius;<br />
Point2 begin = new Point2( x - size, y - size );<br />
Point2 end = new Point2( x + size, y + size );<br />
}<br />
// Return a box that describes the area the object occupies<br />
return new Box2( begin, end );
Extending the Data Domain<br />
}<br />
public void Dispose ( object o, MapRendererContext ctx )<br />
{<br />
return;<br />
}<br />
The second step in rendering a custom domain object in a window<br />
is to register the drawing service with <strong>Ocean</strong> in the module’s<br />
Initialize method.<br />
Type xyzType = typeof(XYZObj);<br />
Type factoryType = typeof(IMapRenderer);<br />
XYZObjMapDisplay mapFactory = new XYZObjMapDisplay();<br />
CoreSystem.Services.AddService(xyzType, factoryType, mapFactory);<br />
When the end user checks the checkbox, the XYZObject is<br />
rendered in the Map window.<br />
Figure 4-8 – XYZ Object in Map window<br />
80
Extending the Data Domain<br />
Saving Custom Domain Objects<br />
A custom domain object may be saved into the Petrel project using<br />
serialization.<br />
Serialization<br />
Serialization has four steps.<br />
1. Mark the class with the Serializable attribute<br />
[Serializable]<br />
public class XYZObj : ...<br />
2. Implement the ISerializable interface<br />
public class XYZObj : ISerializable<br />
3. Implement GetObjectData to serialize the object<br />
public void GetObjectData( SerializationInfo info,<br />
StreamingContext ctx )<br />
{<br />
info.AddValue( “X”, m_x );<br />
info.AddValue( “Y”, m_y );<br />
info.AddValue( “Z”, m_z );<br />
info.AddValue( “Radius”, m_radius );<br />
}<br />
All of the properties which are serialized must be serializable.<br />
4. Write the deserialization constructor.<br />
public XYZObj (SerializationInfo info, StreamingContext ctx )<br />
{<br />
this.m_x = info.GetSingle(“X”);<br />
this.m_y = info.GetSingle(“Y”);<br />
this.m_z = info.GetSingle(“Z”);<br />
this.m_radius = info.GetSingle(“Radius”);
Extending the Data Domain<br />
}<br />
The XYZObj class declaration therefore becomes:<br />
[Serializable]<br />
public class XYZObj : ISerializable<br />
{<br />
float m_x;<br />
float m_y;<br />
float m_z;<br />
float m_radius;<br />
...<br />
// Deserialization Constructor<br />
public XYZObj(SerializationInfo info, StreamingContext ctx)<br />
{<br />
this.m_x = info.GetSingle(“X”);<br />
this.m_y = info.GetSingle(“Y”);<br />
this.m_z = info.GetSingle(“Z”);<br />
this.m_radius = info.GetSingle(“Radius”);<br />
}<br />
...<br />
public void GetObjectData(SerializationInfo info,<br />
StreamingContext context )<br />
{<br />
info.AddValue( “X”, m_x );<br />
info.AddValue( “Y”, m_y );<br />
info.AddValue( “Z”, m_z );<br />
info.AddValue( “Radius”, m_radius );<br />
}<br />
}<br />
When project is saved, the XYZObject will be saved with it.<br />
Reopening the project will load this data back into Petrel.<br />
82