|
|
About an approach for Geomedia based dispatching office creationBy Ivan Tsygoulyov (http://www.i1.f2s.com/) Abstract:This article and its dowloadable code examples illustrates how to add GIS functionality to the existing gas flow modeling system.Once we had a task to add GIS functionality to the existing gas flow modeling system. This system was written on Visual C++ with Stingray's Objective Views library with database storage under MS SQL server. Geomedia Professional v.3.0 was selected as front end application and the main problem was to add existing possibilities and habitual interface to Geomedia environment. This task had to be made with minimal time efforts without extra coding for porting existing application. It was necessary to organize data exchange between C++ project and Geomedia environment. The following approach was selected: we had to write 2 applications first : Geomedia command written on Visual Basic with the help of wizard with easy Geomedia objects access ways and ActiveX object written on Visual C++ with ATL library. The second was selected to overcome known limitations: the disability to write modeless commands and problems with docking toolbars creation. The Geomedia command is based on lab works (4,9) and "Dynamic drawing" command (special thanks to Lars Tungen) and provides data exchange with ActiveX which is installed in the Activate sub as following:
Set goDock = gobjGeoApp.NewDockableControl("GAS.GasEdit", _
"Dynamic Properties", gmDockRight, gmdcDockAlignAny)
SetConnection gobjGeoApp.Document.Connections(1)
goDock.ControlObject.Init Me
With goDock
.AutoStretch = False
.Persistence = gmdcPersistInDoc
.Resizable = True
.UserControl = True
.Visible = True
.Caption = "Parameters"
End With
gobjGeoApp.DockableControls.Append goDock
Init method is called to pass IDispatch of Geomedia command to our ActiveX object for further data exchange. GAS object just stores pointer in variable _geoCommand:
STDMETHODIMP CGasEdit::Init(IDispatch *geoCommand)
{
_geoCommand = geoCommand;
return S_OK;
}
GAS object also has 2 methods for processing Geomedia data: [id(1), helpstring("method DisplayData")] HRESULT DisplayData([in] BSTR type, [in] BSTR id); DisplayData is used for displaying the type of currently highlighted object and the key field value of it.
STDMETHODIMP CGasEdit::DisplayData(BSTR type, BSTR id)
{
lastType = _bstr_t(type);
lastId = _bstr_t(id);
SetDlgItemText(IDC_EDIT1,lastType);
SetDlgItemText(IDC_EDIT2,lastId);
return S_OK;
}
DiplayDataEX is used for modeless dialog creation connected with selected object.
STDMETHODIMP CGasEdit::DisplayDataEx(BSTR type, BSTR id)
{
CInfoDialog* m_dlg;
m_dlg = new CInfoDialog();
m_dlg -> lastType = _bstr_t(type);
m_dlg -> lastId = _bstr_t(id);
m_dlg -> Create(::GetActiveWindow());
m_dlg -> ShowWindow (SW_SHOW);
return S_OK;
}
This dialog was used as extended info panel with graphics in the prototype project. The following code translates Gomedia events to our GAS object:
Private Sub EventControl1_MouseMove(_
ByVal MapviewDispatch As Object, _
ByVal Button As Long, ByVal Key As Long, _
ByVal WindowX As Double, ByVal WindowY As Double, _
ByVal WindowZ As Double, ByVal worldX As Double, _
ByVal worldY As Double, ByVal worldZ As Double)
On Error GoTo ErrorHandler
If MapviewDispatch.HighlightedObjects.Count > 0 Then
Dim Recordset As GRecordset
Set Recordset = MapviewDispatch.HighlightedObjects._
Item(1).Recordset
Recordset.Bookmark = MapviewDispatch.HighlightedObjects._
Item(1).Bookmark
Dim I As Long
For I = 0 To UBound(vFeatures) - 1
If Recordset.GFields(0).SourceTable = vFeatures(I) Then
goDock.ControlObject.DisplayData vFeatures(I), _
Recordset.GFields(sAttributes(I)).Value
Exit For
End If
Next
End If
GoTo Finish
ErrorHandler:
Err.Raise vbObjectError + 103, Err.Source, Err.Description
Finish:
On Error Resume Next
End Sub
Private Sub EventControl1_Click(ByVal MapviewDispatch As Object, _
ByVal Button As Long, _
ByVal Key As Long, ByVal WindowX As Double, _
ByVal WindowY As Double, ByVal WindowZ As Double, _
ByVal worldX As Double, ByVal worldY As Double, _
ByVal worldZ As Double)
On Error GoTo ErrorHandler
If MapviewDispatch.MapViewSelectedObjects.Count > 0 Then
Dim Recordset As GRecordset
Set Recordset = MapviewDispatch.MapViewSelectedObjects._
Item(1).Recordset
Recordset.Bookmark = _
MapviewDispatch.MapViewSelectedObjects.Item(1).Bookmark
Dim I As Long
For I = 0 To UBound(vFeatures) - 1
If Recordset.GFields(0).SourceTable = vFeatures(I) _
Then
goDock.ControlObject.DisplayDataEx vFeatures(I), _
Recordset.GFields(sAttributes(I)).Value
Exit For
End If
Next
End If
GoTo Finish
ErrorHandler:
Err.Raise vbObjectError + 103, Err.Source, Err.Description
Finish:
On Error Resume Next
End Sub
Notice that in first case we use highlited objects and in second case we use selected objects. You can experiment why we do so. The prototype code for this approach was taken from lab work No9. But notice what changes had been made in the SetConnection function:
If sAttributes(I) = "" Then
For Each oFld In moConn.Database.GTableDefs(vFeatures(I)).GFields
If oFld.Name = "id" Or oFld.Name = "Id" Then
sAttributes(I) = oFld.Name
Exit For
End If
Next oFld
End If
We try to find fields named as "id" or "Id" instead of taking first field. Response and Mark subroutines were written as an example of passing data from GAS object to Geomedia command for further processing in the Geomedia environment.
Sub mark(markPoint As Point)
cX = markPoint.X
cY = markPoint.Y
cZ = markPoint.Z
frmDynamicProperties.Timer1.Enabled = True
End Sub
Sub response(elementType As String, elementId As String, _
action As String)
On Error GoTo ErrorHandler
' finding key field name
Dim I As Long
For I = 0 To UBound(vFeatures) - 1
'sAttributes(I) -> key field name
If elementType = vFeatures(I) Then
Exit For
End If
Next
' forming sql query
Dim Filter As String
Filter = "Select * From " & elementType & " Where " & _
sAttributes(I) & " = " & elementId
MsgBox Filter
Dim GSS As GeometryStorageService
Dim RS As GRecordset
Dim GeomObj As Object
Set RS = gobjGeoApp.Document.Connections(1).Database._
OpenRecordset(Filter, 2)
Set GSS = _
gobjGeoApp.CreateService("GeoMedia.GeometryStorageService")
' finding geometry field name
Dim CurrentField As GField
For Each CurrentField In RS.GFields
If CurrentField.Type = gdbSpatial Or CurrentField.Type = _
gdbGraphic Then
Exit For
End If
Next CurrentField
' perfoming transformation
Dim objCSSPipe As CSSTransformPipe
Set objCSSPipe = _
gobjGeoApp.CreateService("GeoMedia.CSSTransformPipe")
Set objCSSPipe.InputRecordset = RS
Set objCSSPipe.CoordSystemsMgr = _
gobjGeoApp.Document.CoordSystemsMgr
objCSSPipe.InputGeometryFieldName = CurrentField.Name
Set RS = objCSSPipe.OutputRecordset
Set objCSSPipe = Nothing
' forming mark point
GSS.GetGeometry RS.GFields(CurrentField.Name), GeomObj
If GeomObj.Type = "OrientedPointGeometry" Or _
GeomObj.Type = "PointGeometry" Or _
GeomObj.Type = "TextPointGeometry" Or _
GeomObj.Type = "RectangleGeometry" Then
mark GeomObj.Origin
Else
If GeomObj.Type = "PolylineGeometry" Or _
GeomObj.Type = "PolygonGeometry" Then
Dim markPoint As New Point
Dim CurrentPoint As Point
markPoint.X = 0#
markPoint.Y = 0#
markPoint.Z = 0#
For Each CurrentPoint In GeomObj.Points
markPoint.X = markPoint.X + CurrentPoint.X
markPoint.Y = markPoint.Y + CurrentPoint.Y
markPoint.Z = markPoint.Z + CurrentPoint.Z
Next CurrentPoint
markPoint.X = markPoint.X / GeomObj.Points.Count
markPoint.Y = markPoint.Y / GeomObj.Points.Count
markPoint.Z = markPoint.Z / GeomObj.Points.Count
mark markPoint
Set markPoint = Nothing
End If
End If
GoTo Finish
ErrorHandler:
Err.Raise vbObjectError + 101, Err.Source, Err.Description
Finish:
Set GSS = Nothing
End Sub
In the Response method we find the cords of the point we want to mark. Global cords variables are changed here which are taken for marking by dynamic draw functions. The Response method is called from C++ code as follows:
LRESULT OnClickedButton1(WORD wNotifyCode, WORD wID, HWND hWndCtl, _
BOOL& bHandled)
{
try
{
GeoMediaCommand::_DynamicPropertiesPtr t;
_geoCommand->QueryInterface (&t);
BSTR p1 = SysAllocString(lastType);
BSTR p2 = SysAllocString(lastId);
BSTR p3 = SysAllocString(L"DO IT");
t->response(&p1,&p2,&p3);
}
catch(_com_error & err)
{
::MessageBox(NULL, (LPCSTR)err.Description(), _
_T("Error in response..."), MB_OK);
}
/*
DISPID dispid;
OLECHAR FAR* szMember = L"response";
_geoCommand->GetIDsOfNames (IID_NULL, &szMember, 1, _
LOCALE_SYSTEM_DEFAULT, &dispid);
DISPPARAMS dispparams ; //= {NULL,NULL,0,0};
dispparams.cArgs = 3;
dispparams.cNamedArgs = 0;
dispparams.rgdispidNamedArgs = NULL;
dispparams.rgvarg = new VARIANTARG[3];
dispparams.rgvarg[0].vt = VT_BSTR;
dispparams.rgvarg[0].bstrVal = SysAllocString(L"DO IT");
dispparams.rgvarg[1].vt = VT_BSTR;
dispparams.rgvarg[1].bstrVal = SysAllocString(lastId);
dispparams.rgvarg[2].vt = VT_BSTR;
dispparams.rgvarg[2].bstrVal = SysAllocString(lastType);
VARIANT Result;
Result.vt = VT_BOOL;
Result.boolVal = VARIANT_FALSE;
EXCEPINFO excep;
UINT uArgErr;
_geoCommand->Invoke(dispid,IID_NULL,LOCALE_SYSTEM_DEFAULT,_
DISPATCH_METHOD,&dispparams,&Result,&excep,&uArgErr);
*/
return 0;
}
The second (Invoke based) call can be used when we don't import Geomedia command dll. I use it in order not to recompile C++ project after I have made changes in Basic project, but in last version it is necessary to use "import" directive for faster execution without "invoke". (see MSDN)
#import "..\dynprops\bin\dynamicproperties.dll" So in this article I tried to show how to add Geomedia functionality for existing Visual C++ projects or vice versa :) The RTF version of the article and the complete source code are contained in the zip file Geomedia based dispatching office creation. The commands and applications were created using GeoMedia 3, Visual Basic and Visual C++. |
|
|
|
|
|
© 2001 MyGeoMedia.com - Webmaster: webmaster@mygeomedia.com |