Creating custom objects in C++
MapObjects2 allows you to write your own symbol rendering code in an integrated manner. To do so, you write an OLE (COM) class that implements a well defined API. MapObjects2 now supports five custom interfaces:
ICustomFill
ICustomLine
ICustomMarker
ICustomProjection
ICustomRenderer
Since MapObjects2 uses OLE interfaces to interact with custom symbols, you do not need any source code or libraries to build your custom symbols. All the definitions you need are distributed in a type library (AfCust20.tlb). This file can be found in the "..\Common Files\ESRI\" directory.
When using Visual C++ the easiest way to build COM objects that implement these interfaces is to use the ATL. ATL is the Active Template Library, a set of template-based C++ classes with which you can easily create small, fast (COM) objects.
The following steps demonstrate how to create a custom point symbol; they can easily be modified to provide support for custom fill and line symbols, likewise custom renderers and projections. A similar description for creating custom projections exists in the on-line help.
When distributing your application, ensure all necessary DLL's are distributed - your ATL COM application will have dependancies on Visual Studio 6.0 DLL's.
Create a ATL COM DLL, using the ATL COM App wizard as below.
Example of how to create a custom point symbol server in Visual Studio 6.0.
1. File->New...
a) Select Projects tab
c) Enter project name, for example, CustomSymbol
d) Press OK
2) ATL COM AppWizard - Step 1 of 1
a) Select Dynamic Link Library radio button
b) Check MFC support.
c) Press Finish
3) New Project Information
a) Press OK
4) Insert->New ATL Object...
a) Select "Objects" in list of Categories
b) Select "Simple Object" in list of Objects
c) Press Next
5) ATL Object Wizard Properties
a) Select Names tab
b) Enter the name of your point symbol class in Short Name, for example MyPointSymbol
c) No change is necessary to anything on the Attributes tab
d) Press OK
6) Workspace window, ClassView tab
a) Expand the list of classes in your project
b) You will see a class name starting with a "C", like CMyPointSymbol .
c) Right-click on this class, and choose Implement Interface...
d) Press OK in the warning dialog, this is
telling you that you need an ‘idl’ file.
7) Browse Type Libraries
a) Press Browse...
b) Find AFCust20.tlb and select it. This is typically in ‘..\Common Files\ESRI\’
c) Press Open
8) Implement Interface
a) You will see a list of the interfaces supported by AFCustom20.
b) To implement the point symbol interface, check the box next to ICustomMarker .
c) Press OK
9) Workspace window, ClassView tab
a) Double-click on the CMyPointSymbol class
b) This will open the file MyPointSymbol.h in a window.
10) MyPointSymbol.h
a) Locate the implementations of SetupDC, ResetDC, and Draw .
These will look like this:
STDMETHOD(SetupDC)(LONG hDC, DOUBLE dpi, IDispatch * pBaseSym)
{
return E_NOTIMPL;
}
STDMETHOD(ResetDC)(LONG hDC)
{
return E_NOTIMPL;
}
STDMETHOD(Draw)(LONG hDC, LONG x, LONG y)
{
return E_NOTIMPL;
}
b) Add a private member variable. In the workspace window, ClassView Tab, highlight the CMyPointSymbol class. Right click on the mouse and select ‘Add member variable’ . Enter type as CPen* and name as m_oldPen. This will be use to hold your old pen object that is
returned from CDC::SelectStockObject
c) Add your code to implement your custom point symbol. The following example is very basic – it simply draws each point symbol as three concentric squares.
STDMETHOD(SetupDC)(LONG hDC, DOUBLE dpi, IDispatch * pBaseSym)
{
CDC* pcdc = CDC::FromHandle((HDC)hDC);
m_oldPen =
CPen*)pcdc->SelectStockObject(BLACK_PEN);
return S_OK;
}
STDMETHOD(ResetDC)(LONG hDC)
{
CDC* pcdc = CDC::FromHandle((HDC)hDC);
CPen* temp = pcdc->SelectObject(m_oldPen);
temp->DeleteObject(); return S_OK;
}
STDMETHOD(Draw)(LONG hDC, LONG x, LONG y)
{
CDC* pcdc;
pcdc =
::CDC::FromHandle((HDC)hDC);
CPoint pt;
for (int i= 0; i<10; i+=2)
{
pt.x = x-i;
pt.y = y-i;
pcdc->MoveTo(pt);
pcdc->LineTo(pt.x+(2*i),pt.y);
pcdc->LineTo(pt.x+(2*i),pt.y+(2*i));
pcdc->LineTo(pt.x,pt.y+(2*i));
pcdc->LineTo(pt);
}
return S_OK;
}
11) Build->Set Active Configuration
a) Select Win32 Release MinDependency
12) Build->Rebuild All
b) Build your dll.
13) You can now use your new COM object within your application
Using a custom point symbol in your project
This example uses Visual Basic to demonstrate how to use your new COM point symbol object. Create a new project with the MapObjects2 control loaded. Add a map control to the form and add a points shape file layer ("..\data\world\cities.shp" ).
To load your CustomlSymbol into your Visual Basic project select the Project->References option and tick the appropriate checkbox. Look for "Custom Symbol 1.0 Type Library" . If VB cannot find the type library, you may need to browse for it. Look for "CustomSymbol.tlb"
Private Sub Command1_Click()
Dim oLayer As MapObjects2.MapLayer
Set oLayer = Map1.Layers(0)
Dim myMarker As New CUSTOMSYMBOLLib.MyPointSymbol
Dim oSymbol As New MapObjects2.Symbol
oLayer.Symbol.Custom = myMarker
Map1.Refresh
End Sub
Converting from a MapObjects 1.x Custom Object
The following steps outline what you need to do to convert your MapObjects 1.x Custom Object DLL, written in Visual C++, to allow it to run with MapObects 2.x.
- Update code. This applies to both your Custom Object
DLL and the application. Open a code window and select "Replace" from the
"Edit" menu. Replace the string
"MapObjects." with "MapObjects2." . Repeat the process, replacing "MoPlus." with "MapObjects2." (See Notes below).
- Replace any reference to
"AFCust10" with "AFCust20"
- If you are making function calls into the MapObjects2
OCX – perhaps by creating Dispatch wrapper classes – then the Dispatch ID’s
for these function calls must be updated. You can obtain the correct VTble
entry point Dispatch ID for the required function in the
"map.cpp" file supplied in the "..\MFC\Common\" directory. (All the MapObjects2.x C++ wrapper classes
are in this directory).
- You must rebuild your DLL, and any application that uses it must also be changed. Modifications to the end-application include:
- Ensuring that the application uses the MapObjects
2.x control.
- Update code to replace "MapObjects." with "MapObjects2.".
- Ensuring that the application references your new, updated Custom DLL.
Notes:
The MoPlus GroupRenderer and LabelPlacer objects have been incorporated into core MapObjects. The PathFinder object has not, therefore all references to the PathFinder should be removed from a MapObjects 2.0 application.
MapObjects 1.x and 2.0 both have dependencies on some of the same DLLs, but those DLLs installed by MapObjects 2.0 are a more recent version. To avoid your development environment loading the older DLLs into memory when updating your project, ensure that you run a MapObjects 2.0 application once before running any MapObjects 1.x applications. The more recent DLLs will be loaded into memory, which are backward compatible with MapObjects 1.x, and both MapObjects 1.x and 2.0 applications will run successfully.
|