About an approach for Geomedia based dispatching office creation

By

Ivan Tsygoulyov (http://www.i1.f2s.com/)
BelInfoSoft ltd., Belgorod, Russia

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);
[id(3), helpstring("method DisplayDataEx")] HRESULT DisplayDataEx([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++.

Print friendly version Print friendly version

 

© 2001 MyGeoMedia.com - Webmaster: webmaster@mygeomedia.com