Supported with:
Additional library information: Contents, Object Model Diagram
See the following sections for more information about this namespace:
For further information see:
Military Symbols for Land Based Systems - APP-6A
Using military symbology and symbol-based graphics
Sample: Displaying symbol-based MOLE graphics on a MapControl
Sample: Decluttering MOLE graphics using leadering and stacking
Sample: Arranging MOLE graphics using manual decluttering
Sample: Using MOLE symbol-based graphics with interactive maps
Sample: Displaying MOLE symbology with the GlobeControl
For further information see:
Defense solutions coordinate API input-output formats
Sample: Locate coordinates
The type of GeoPolyline that's created is determined by the SpecialGeolineType property, which uses the cjmtkSGType enumeration. Additional properties controlling the number of intermediate vertices generated between the start and end points of the GeoPolyline can be specified as well. These are the MaxPercent and MaxStepSize properties. With the MaxPercent property, a percentage of the total distance of the line can be specified, and a vertex point is placed along the line at distance intervals corresponding to this value. In other words, if a value of 0.1, or 10 percent, is specified, nine vertices will be created, dividing the line into ten segments. The MaxStepSize property defines the maximum distance in meters between vertices along the line. If neither MaxPercent nor MaxStepSize is specified, then MaxPercent is used and defaults to a value of 0.05, or 5 percent.
For further information see:
Sample: Geodesy MapControl
Using military symbology and symbol-based graphics
Defense solutions coordinate API input-output formats
Sample: Decluttering MOLE graphics using leadering and stacking
Sample: Arranging MOLE graphics using manual decluttering
Sample: Using MOLE symbol-based graphics with interactive maps
Sample: Displaying MOLE symbology with the GlobeControl
Sample: Displaying symbol-based MOLE graphics on a MapControl
Sample: Locate coordinates
Sample: Geodesy MapControl
- Engine with Military Analyst
- Engine with Military Overlay Editor
- ArcView with Military Analyst
- ArcView with Military Overlay Editor
- ArcEditor with Military Analyst
- ArcEditor with Military Overlay Editor
- ArcInfo with Military Analyst
- ArcInfo with Military Overlay Editor
Additional library information: Contents, Object Model Diagram
The DefenseSolutions library contains objects for building defense applications that provide military symbology, coordinate conversion, creation of geodetically correct geometries, and other defense analysis tools. The DefenseSolutions library combines two distinct libraries from previous releases of ArcGIS—the Military Overlay Editor (MOLE) library and the Military Analyst library.
For getting started information for developers, such as release notes, system requirements, and basic concepts for developing with the DefenseSolutions library, see the 'Developing with Defense Solutions' chapter in the Installing and Getting Started with Defense Solutions guide.
See the following sections for more information about this namespace:
- New at ArcGIS 10
- Displaying and editing military symbology
- Converting coordinates
- Creating geodetically correct geometric objects
- Controlling the Military Analyst GeoSym Renderer
- Creating and displaying Military Analyst layers
New at ArcGIS 10
Many of the ArcGIS Military Analyst and MOLE extension application programming interface (API) objects you have used in the past have equivalent or near equivalent objects in the ArcGIS 10 ArcObjects API. These new objects should now be used and do not require a Military Analyst extension or MOLE extension license. See Migrating from Military Analyst and MOLE for information on how to accomplish tasks in ArcGIS 10 that previously required the Military Analyst and MOLE APIs.
Displaying and editing military symbology
Using the MOLE application programming interface (API), you can incorporate MIL-STD-2525B Change 2 and APP-6A symbology and MOLE functionality into your applications. MOLE organizes military symbology into two categories: force element graphics and tactical graphics. Force element graphics are point objects that represent military units, installations, and equipment. Tactical graphics are point, line, or polygon objects that represent command/control, mission planning, and operations. These can represent military bases, facilities, mission guidance, control measures, and obstacles. For more details on force element graphics and tactical graphics, see the Using military symbology and symbol-based graphics topic.
To display and edit military symbology programmatically, you can use the following two approaches:
- Managing MOLE graphics as standard cartographic and display ArcObjects
- Managing MOLE graphics as ArcGIS features
The MOLE API enables you to access and use essential MOLE functionality within the standard ArcGIS cartographic and display frameworks. This feature is especially beneficial to developers who are new to MOLE development and need basic MOLE functionality in their applications. This approach allows you to quickly develop applications that require dynamic, interactive, and easy-to-use symbology.
This approach is ideal for developing applications that meet any of the following criteria:
-
There is need for a great degree of interaction with the displayed MOLE symbols
By providing MOLE display functionality in a standard ArcGIS ISymbol-based implementation, MOLE data can be attached to standard ArcObjects IElements. These can then be selected, moved, angled, and so on. -
There is need for frequent updates to the displayed MOLE symbols
Since ArcObjects IElements exist in memory as part of the map display’s graphics container, it is possible to update them more frequently on the display. With this approach, there is no need to write updates to an on-disk feature class stored in a geodatabase. -
There is minimal need to customize the appearance of MOLE symbols
This approach allows a small degree of customization to the appearance of MOLE symbology. However, these provisions are currently minimal—limited to text size and fill color.
Symbols and groups
The MOLE API provides two main types for managing MOLE graphics as standard ArcObjects types: Symbols and Groups. Symbols consists of the MoleSymbol base class and three derived classes: MoleMarkerSymbol, MoleLineSymbol, and MoleFillSymbol. Groups consists of the MoleGroupElement class, which provides decluttering capabilities.
The following illustration shows the class hierarchy and interfaces provided by the Symbols type:
The following illustration shows the interfaces provided by the Groups type:
The MoleSymbol class allows you to create MOLE graphics and display them on a map, without the need to store them as feature classes. With this class, you can create and render a MOLE graphic with just two pieces of information: a valid symbol identification code and a map coordinate. This class implements the IMoleSymbol interface, which provides properties that control the appearance of MOLE graphics, and the IMoleSymbolImportExport interface, which provides methods that allow for a MoleSymbol object to be populated from a MOLE cached graphic object, or vice versa.
The details of the IMoleSymbol and IMoleSymbolImportExport interfaces are shown in the following diagrams:
There are three classes that derive from the MoleSymbol class: MoleMarkerSymbol, MoleLineSymbol, and MoleFillSymbol.
MoleMarkerSymbol
The MoleMarkerSymbol class is used to render both MOLE force element and point-based tactical graphics. The details of the MoleMarkerSymbol class are shown in the following diagram:
In the case of force element graphics, there are extra Boolean properties (ShowFill, ShowFrame, ShowIcon) provided to control the symbol’s appearance. For more information on these properties, see Table IX of the MIL-STD-2525B Change 2 documentation. The following code examples show how to use the MoleMarkerSymbol class:
[VB.NET]
' Helper method.
Private Function MakeMoleElement(ByVal SymbolID As String, ByVal lat As Double, ByVal lon As Double) As IElement
' Create the point.
Dim lox As IPoint = New PointClass()
lox.PutCoords(lon, lat)
' Create the symbol.
Dim ms As IMoleSymbol = New MoleMarkerSymbolClass()
ms.SymbolID = SymbolID
' Create the MarkerElement and attach the symbol.
Dim [Me] As IMarkerElement = New MarkerElementClass()
[Me].Symbol = TryCast(ms, IMarkerSymbol)
' Attach the geometry.
Dim em As IElement = TryCast([Me], IElement)
em.Geometry = TryCast(loki, IGeometry)
Return em
End Function
' Application code.
Private mme As IElement = MakeMoleElement("SFGPUCAWW------", 45, -90)
m_ActiveView.GraphicsContainer.AddElement(mme, 0)
[C#]
// Helper method.
private IElement MakeMoleElement(string SymbolID, double lat, double lon)
{
// Create the point.
IPoint lox = new PointClass();
lox.PutCoords(lon, lat);
// Create the symbol.
IMoleSymbol ms = new MoleMarkerSymbolClass();
ms.SymbolID = SymbolID;
// Create the MarkerElement and attach the symbol.
IMarkerElement me = new MarkerElementClass();
me.Symbol = ms as IMarkerSymbol;
// Attach the geometry.
IElement em = me as IElement;
em.Geometry = loki as IGeometry;
return em;
}
// Application code.
IElement mme = MakeMoleElement("SFGPUCAWW------", 45, - 90);
m_ActiveView.GraphicsContainer.AddElement(mme, 0);
[VCPP]
// Creates a MoleMarkerSymbol and attaches it to a MarkerElement.
IElementPtr MakeMoleElement(CComBSTR SymbolID, double lat, double lon)
{
// create the point
IPointPtr lox(CLSID_Point);
lox->PutCoords(lon, lat);
// create the symbol
IMoleSymbolPtr ms(CLSID_MoleMarkerSymbol);
ms->put_SymbolID(SymbolID);
// create the MarkerElement & attach the symbol
IMarkerElementPtr me(CLSID_MarkerElement);
IMarkerSymbolPtr markerSymbol(ms);
me->put_Symbol(markerSymbol);
// now attach the geometry
IElementPtr elem(me); // as IElement;
elem->put_Geometry(lox);
return elem;
}
// Client code
IElementPtr mme = MakeMoleElement(L "SFGPUCAWW------", 45, - 90);
m_ipActiveView->get_GraphicsContainer()->AddElement(mme, 0);
MoleLineSymbol and MoleFillSymbol
MoleLineSymbol and MoleFillSymbol are the classes used to render MOLE polyline- and polygon-based tactical graphics, respectively. The details of these two classes are shown in the following diagrams:
These classes are similar to one another and to MoleMarkerSymbol. The primary difference between MoleLineSymbol and MoleFillSymbol has to do with the geometry types with which they are used. MoleLineSymbol must be used with an ArcObjects LineElement containing a Polyline geometry, and MoleFillSymbol must be used with an ArcObjects PolygonElement containing a Polygon geometry. To prevent erroneous mixing of MOLE symbols with invalid geometry types, MoleLineSymbol also implements the ILineSymbol interface. Likewise, MoleFillSymbol implements the IFillSymbol interface.
The following code example shows the steps necessary to create a MoleLineSymbol, attach it to a LineElement, and add it to a map:
[VB.NET]
Private Sub DrawMoleLineElement(ByVal Line As IPolyline)
' Create the symbol (use a sample SIC).
Dim ms As IMoleSymbol = New MoleLineSymbolClass()
ms.SymbolID = "GUTPF---------X"
ms.TextSize = 2.0
' Create the LineElement and attach the symbol.
Dim lem As ILineElement = New LineElementClass()
lem.Symbol = TryCast(ms, ILineSymbol)
' Attach the geometry.
Dim em As IElement = TryCast(lem, IElement)
em.Geometry = TryCast(Line, IGeometry)
' Add it to the active view.
m_ActiveView.GraphicsContainer.AddElement(em, 0)
End Sub
[C#]
private void DrawMoleLineElement(IPolyline line)
{
// Create the symbol (use a sample SIC).
IMoleSymbol ms = new MoleLineSymbolClass();
ms.SymbolID = "GUTPF---------X";
ms.TextSize = 2.0;
// Create the LineElement and attach the symbol.
ILineElement lem = new LineElementClass();
lem.Symbol = ms as ILineSymbol;
// Attach the geometry.
IElement em = lem as IElement;
em.Geometry = line as IGeometry;
// Add it to the map.
m_ActiveView.GraphicsContainer.AddElement(em, 0);
}
[VCPP]
void DrawMoleLineElement(IPolylinePtr line)
{
// create the symbol
IMoleSymbolPtr ms(CLSID_MoleLineSymbol);
ms->put_SymbolID(L "GUTPF---------X");
ms->put_TextSize(2.0);
// create the LineElement & attach the symbol
ILineElementPtr lem(CLSID_LineElement);
ILineSymbolPtr lineSym = ms;
lem->put_Symbol(lineSym);
// now attach the geometry
IElementPtr em = lem;
em->put_Geometry(line);
// add to the map
m_ipActiveView()->get_GraphicsContainer()->AddElement(em, 0);
}
The MoleGroupElement class (the only member of this type) provides decluttering capabilities, such as the ability to toggle manual decluttering, leadering, or stacking. MoleGroupElement is designed to work like a standard ArcObjects GroupElement, except that it only contains MarkerElements with MoleMarkerSymbols attached. It implements several interfaces to manage its varying decluttering capabilities.
The details of the MoleGroupElement class are shown in the following diagram:
IMoleGroupElement
The IMoleGroupElement interface provides an enumeration property that allows you to choose the type of decluttering to use with the MoleGroupElement, as well as a Boolean property that allows you to toggle decluttering.
The following code example shows how to create a MoleGroupElement and how to add MoleMarkerSymbol-based MarkerElements to it. It also demonstrates the use of IMoleDeclutterElement to set the declutter option and to toggle decluttering:
[VB.NET]
Private Sub AddMoleGroupElement()
' Create the group element.
Dim gem As IMoleGroupElement = New MoleGroupElementClass()
' Add a few symbols to it.
Dim group As IGroupElement = TryCast(gem, IGroupElement)
group.AddElement(MakeMoleElement("SFGPUCAWW------", 45, -90))
group.AddElement(MakeMoleElement("SFGP-----------", 46, -91))
' Enable decluttering and select the option.
gem.DeclutterOption = moleDeclutterOptionEnum.moleDeclutterNone
gem.EnableDeclutter = True
' Add the group element to the map.
m_ActiveView.GraphicsContainer.AddElement(TryCast(gem, IElement), 0)
End Sub
' Helper method.
Private Function MakeMoleElement(ByVal SymbolID As String, ByVal lat As Double, ByVal lon As Double) As IElement
' Create the point.
Dim lox As IPoint = New PointClass()
lox.PutCoords(lon, lat)
' Create the symbol.
Dim ms As IMoleSymbol = New MoleMarkerSymbolClass()
ms.SymbolID = SymbolID
' Create the MarkerElement and attach the symbol.
Dim [Me] As IMarkerElement = New MarkerElementClass()
[Me].Symbol = TryCast(ms, IMarkerSymbol)
' Attach the geometry.
Dim em As IElement = TryCast([Me], IElement)
em.Geometry = TryCast(loki, IGeometry)
Return em
End Function
[C#]
private void AddMoleGroupElement()
{
// Create the group element.
IMoleGroupElement gem = new MoleGroupElementClass();
// Add a few symbols to it.
IGroupElement group = gem as IGroupElement;
group.AddElement(MakeMoleElement("SFGPUCAWW------", 45, - 90));
group.AddElement(MakeMoleElement("SFGP-----------", 46, - 91));
// Enable decluttering and select the option.
gem.DeclutterOption = moleDeclutterOptionEnum.moleDeclutterNone;
gem.EnableDeclutter = true;
// Add the group element to the map.
m_ActiveView.GraphicsContainer.AddElement(gem as IElement, 0);
}
// Helper method.
private IElement MakeMoleElement(string SymbolID, double lat, double lon)
{
// Create the point.
IPoint lox = new PointClass();
lox.PutCoords(lon, lat);
// Create the symbol.
IMoleSymbol ms = new MoleMarkerSymbolClass();
ms.SymbolID = SymbolID;
// Create the MarkerElement and attach the symbol.
IMarkerElement me = new MarkerElementClass();
me.Symbol = ms as IMarkerSymbol;
// Attach the geometry.
IElement em = me as IElement;
em.Geometry = loki as IGeometry;
return em;
}
[VCPP]
void AddMoleGroupElement()
{
// create the group element
IMoleGroupElementPtr gem(CLSID_MoleGroupElement);
// add a few symbols to it
IGroupElementPtr gElem(gem);
IGroupElementPtr group = gElem;
CComBSTR x("");
group->AddElement(MakeMoleElement(L "SFGPUCAWW------", 45, - 90));
group->AddElement(MakeMoleElement(L "SFGP-----------", 46, - 91));
// enable decluttering and select the option
gem->put_DeclutterOption(moleDeclutterNone);
gem->put_EnableDeclutter(VARIANT_TRUE);
// add the group element to the map
m_ActiveView.GraphicsContainer.AddElement(gem, 0);
}
Manual decluttering
The manual decluttering feature of MoleGroupElement provides the means by which you can apply customized, specific decluttering on a group of MOLE graphics. Manual decluttering is achieved using the IMoleDeclutterElement interface. The following graphic shows an example of manual decluttering:
Leadering
The leadering capability of MoleGroupElement allows you to declutter MOLE graphics by displaying them in leader lines. Leadering is achieved using the IMoleLeaderElement interface. The following graphic shows an example of leadering:
Stacking
The stacking capability of MoleGroupElement allows you to declutter MOLE graphics by displaying them in stack clusters. Stacking is achieved using the IMoleStackElement interface. The following graphic shows an example of stacking:
After adding the MOLE graphics to MoleGroupElement using the IGroupElement interface, the only additional property that needs to be set for stacking is the StackQuadrant. As with the LeaderQuadrant property, the StackQuadrant property controls the geographic direction in which the stack is layered. For more information, see the moleQuadrantEnum documentation.
The following code example shows how to implement leadering and stacking:
[VB.NET]
Private Sub LeaderGroupElement(ByVal mge As IMoleGroupElement)
' Switch to leadering.
mge.DeclutterOption = moleDeclutterOptionEnum.moleDeclutterLeader
' Set the leadering properties (use default black AnchorSymbol and LineSymbol).
Dim mle As IMoleLeaderElement = TryCast(mge, IMoleLeaderElement)
mle.LeaderQuadrant = moleQuadrantEnum.moleQuadrantUR
' Create the anchor and base points.
Dim anchor As IPoint = New PointClass()
anchor.PutCoords(0, 0)
Dim basep As IPoint = New PointClass()
basep.PutCoords(0.05, 0.05)
' Set the anchor and base locations.
mle.Anchor = anchor
mle.Base = basep
' Set the leader visibility.
mle.LeaderVisible = True
End Sub
Private Sub StackGroupElement(ByVal mge As IMoleGroupElement)
' Switch to stacking.
mge.DeclutterOption = moleDeclutterOptionEnum.moleDeclutterStack
' Set the stacking property.
Dim mse As IMoleStackElement = TryCast(mge, IMoleStackElement)
mse.StackQuadrant = moleQuadrantEnum.moleQuadrantLR
End Sub
[C#]
private void LeaderGroupElement(IMoleGroupElement mge)
{
// Switch to leadering.
mge.DeclutterOption = moleDeclutterOptionEnum.moleDeclutterLeader;
// Set the leadering properties (use default black AnchorSymbol and LineSymbol).
IMoleLeaderElement mle = mge as IMoleLeaderElement;
mle.LeaderQuadrant = moleQuadrantEnum.moleQuadrantUR;
// Create the anchor and base points.
IPoint anchor = new PointClass();
anchor.PutCoords(0, 0);
IPoint basep = new PointClass();
basep.PutCoords(0.05, 0.05);
// Set the anchor and base locations.
mle.Anchor = anchor;
mle.Base = basep;
// Set the leader visibility.
mle.LeaderVisible = true;
}
private void StackGroupElement(IMoleGroupElement mge)
{
// Switch to stacking.
mge.DeclutterOption = moleDeclutterOptionEnum.moleDeclutterStack;
// Set the stacking property.
IMoleStackElement mse = mge as IMoleStackElement;
mse.StackQuadrant = moleQuadrantEnum.moleQuadrantLR;
}
[VCPP]
// Leadering
void LeaderGroupElement(IMoleGroupElementPtr mge)
{
// switch to leadering
mge->put_DeclutterOption(moleDeclutterLeader);
// set leadering properties (use default black AnchorSymbol & LineSymbol)
IMoleLeaderElementPtr mle(mge);
mle->put_LeaderQuadrant(moleQuadrantUR);
// create the anchor and base points
IPointPtr anchor(CLSID_Point);
anchor->PutCoords(0, 0);
IPointPtr basep(CLSID_Point);
basep->PutCoords(0.05, 0.05);
// set the anchor & base locations
mle->put_Anchor(anchor);
mle->put_Base(basep);
// set the leader visibility
mle->put_LeaderVisible(VARIANT_TRUE);
}
// Stacking
void StackGroupElement(IMoleGroupElementPtr mge)
{
// switch to stacking
mge->put_DeclutterOption(moleDeclutterStack);
// set stacking property
IMoleStackElementPtr mse(mge);
mse->put_StackQuadrant(moleQuadrantLR);
}
If your development requirements call for the flexibility and built-in functionality provided by ArcGIS features in conjunction with standard military symbology, you can benefit from MOLE's ability to store symbol data in ArcGIS feature classes or layers.
Key advantages
The key advantages of storing symbol data in ArcGIS feature classes or layers are as follows:
- MOLE data can be stored in any standard ArcGIS geodatabase—File geodatabase (FGDB), personal geodatabase (PGDB), ArcSDE, or shapefiles
- MOLE data can be consumed quickly and displayed seamlessly in standard ArcGIS data viewers such as ArcMap and ArcGlobe
- MOLE data can be converted between the various ArcGIS geodatabase formats using standard geoprocessing tools
- Provides users with a high degree of customization of the display of MOLE symbology
- Currently the only approach for displaying MOLE data in three dimensions (3D)
Pathways to MOLE development using ArcGIS features
The following are the three main pathways when developing applications using MOLE features:
- Managing MOLE graphics as MOLE layers
- Managing MOLE graphics as display lists
- Managing MOLE graphics as cached graphics
The following sections describe each pathway in detail.
A MOLE layer is a custom layer created using the MOLE tools in ArcGIS Desktop. It contains a collection of features that have a MIL-STD-2525B Change 2 or APP-6A Symbol ID value and point, line, or polygon geometry type associated with it. Although this approach provides less control over the appearance of individual graphics, as well as the overhead of accessing a geodatabase, it is ideal if your application already uses a back end geodatabase for data storage and you want the benefits of ArcGIS feature layers. This approach also requires the least development effort.
MOLE symbols can be contained in shapefiles, coverages, and personal or enterprise geodatabases. Tables containing a Symbol ID field with valid codes can be converted to a feature class, and MOLE displays the appropriate military symbols. MOLE uses the extension class ID to identify MOLE data sources. MOLE identifies with and renders any dataset presenting the proper extension class ID. For details on how to create MOLE layers, see the MOLE Tutorial chapter in the Installing and Getting Started with Defense Solutions guide.
MOLE allows you to tag any feature class as an APP-6A feature class or a MIL-STD-2525B Change 2 feature class. Once a feature class has been tagged in this way, MOLE objects can use that information when working with those feature classes. MOLE provides the ForceElementLayer and TacticalGraphicLayer classes to access and manipulate MOLE feature layers.
Displaying ForceElementLayers and TacticalGraphicLayers on a map
The ForceElementLayer and TacticalGraphicLayer classes are the key MOLE layer classes. Both of these types bind to ArcGIS feature layers and allow you to open and load existing MOLE feature layers or classes saved in a personal or enterprise geodatabase onto an ArcGIS Active View. These classes implement the IForceElementLayer and ITacticalGraphicLayer interfaces, respectively. The following diagrams show these interfaces in detail:
The IForceElementLayer interface has a DisplayList member that allows you to store force element graphics in a collection and perform operations on them as a group. This feature enables the ForceElementLayer class to provide decluttering capabilities.
The program flow for loading a MOLE layer onto a map or globe is as follows:
- Load an IFeatureLayer containing MOLE attributes.
- Attach the IFeatureLayer to a MOLE ForceElementLayer (and MOLE renderer).
- Add the ForceElementLayer to a map or globe object.
The following code example shows how to display MOLE layers in an ActiveView:
[VB.NET]
Private Sub btnLoadFE_Click(ByVal sender As Object, ByVal e As EventArgs)
' Open the geodatabase and get a workspace.
Dim dbPath As String = "..\..\data\MilitaryOverlayEditor\mole_data.mdb"
Dim wsf As IWorkspaceFactory = New AccessWorkspaceFactoryClass()
Dim ws As IWorkspace = wsf.OpenFromFile(dbPath, 0)
' Get a feature workspace and open the feature class.
Dim fcName As String = "force_element"
Dim featWorkspace As IFeatureWorkspace = TryCast(ws, IFeatureWorkspace)
Dim fc As IFeatureClass = featWorkspace.OpenFeatureClass(fcName)
' Load the feature class onto the map.
Dim basicMap As IBasicMap = TryCast(m_ActiveView.FocusMap, IBasicMap)
LoadMOLEForceElementLayer(fc, basicMap, "MOLE Force Element", .05, True)
' Size is ratio of display height.
End Sub
' Load a MOLE feature class as a MOLE Force Element layer into the current IBasicMap (Map, Globe, or Scene).
Public Sub LoadMOLEForceElementLayer(ByVal featureClass As IFeatureClass, ByVal basicMap As IBasicMap, ByVal layerName As String, ByVal Size As Double, ByVal sizeIsRatio As Boolean)
' True if size is ratio of the display height.
' Get a geo feature layer interface from the input Feature Class.
Dim geoFeatureLayer As IGeoFeatureLayer = New TryCast(FeatureLayer(), IGeoFeatureLayer)
geoFeatureLayer.FeatureClass = featureClass
' Get a MOLE cached graphic feature layer interface from the geo feature layer.
Dim cachedGraphicFeatureLayer As ICachedGraphicFeatureLayer = New TryCast(ForceElementLayerClass(), ICachedGraphicFeatureLayer)
cachedGraphicFeatureLayer.FeatureLayer = geoFeatureLayer
' Set the symbol size and SizeIsRatio properties of the layer.
Dim forceElementLayer As IForceElementLayer = TryCast(cachedGraphicFeatureLayer, IForceElementLayer)
forceElementLayer.Size = Size
forceElementLayer.SizeIsRatio = sizeIsRatio
' Add the Force Element layer to the map.
Dim layer As ILayer = TryCast(cachedGraphicFeatureLayer, ILayer)
layer.Name = layerName
basicMap.AddLayer(layer)
End Sub
[C#]
private void btnLoadFE_Click(object sender, EventArgs e)
{
// Open the geodatabase and get a workspace.
string dbPath = @"..\..\data\MilitaryOverlayEditor\mole_data.mdb";
IWorkspaceFactory wsf = new AccessWorkspaceFactoryClass();
IWorkspace ws = wsf.OpenFromFile(dbPath, 0);
// Get a feature workspace and open the feature class.
string fcName = "force_element";
IFeatureWorkspace featWorkspace = ws as IFeatureWorkspace;
IFeatureClass fc = featWorkspace.OpenFeatureClass(fcName);
// Load the feature class onto the map.
IBasicMap basicMap = m_ActiveView.FocusMap as IBasicMap;
LoadMOLEForceElementLayer(fc, basicMap, "MOLE Force Element", // Layer name.
.05, // Size of graphics.
true); // Size is ratio of display height.
}
// Load a MOLE feature class as a MOLE Force Element layer into the current IBasicMap (Map, Globe, or Scene).
public void LoadMOLEForceElementLayer(IFeatureClass featureClass,
// The Feature Class to load.
IBasicMap basicMap, // The map, scene, or globe.
String layerName, // The name to assign to the layer.
Double size, // The size of graphics in map units.
Boolean sizeIsRatio) // True if size is ratio of the display height.
{
// Get a geo feature layer interface from the input Feature Class.
IGeoFeatureLayer geoFeatureLayer = new FeatureLayer()as IGeoFeatureLayer;
geoFeatureLayer.FeatureClass = featureClass;
// Get a MOLE cached graphic feature layer interface from the geo feature layer.
ICachedGraphicFeatureLayer cachedGraphicFeatureLayer = new
ForceElementLayerClass()as ICachedGraphicFeatureLayer;
cachedGraphicFeatureLayer.FeatureLayer = geoFeatureLayer;
// Set the symbol size and SizeIsRatio properties of the layer.
IForceElementLayer forceElementLayer = cachedGraphicFeatureLayer as
IForceElementLayer;
forceElementLayer.Size = size;
forceElementLayer.SizeIsRatio = sizeIsRatio;
// Add the Force Element layer to the map.
ILayer layer = cachedGraphicFeatureLayer as ILayer;
layer.Name = layerName;
basicMap.AddLayer(layer);
}
[VCPP]
// Load a Force Element layer onto a map
void LoadFELayer(IFeatureClassPtr ipFC, IBasicMapPtr ipBasicMap)
{
IGeoFeatureLayerPtr ipFeatureLayer(CLSID_FeatureLayer);
HRESULT hr = ipFeatureLayer->putref_FeatureClass(ipFC);
//Create a MOLE layer and attach the feature layer to it.
ICachedGraphicFeatureLayerPtr ipLayer(CLSID_ForceElementLayer);
ipLayer->put_FeatureLayer(ipFeatureLayer);
//Set the size for symbols in the layer.
IForceElementLayerPtr pForceElementLayer = ipLayer;
hr = ipForceElementLayer->put_Size(0.05);
ILayerPtr ipL = ipLayer;
ipL->put_Name(CComBSTR("My MOLE Layer"));
ipBasicMap->AddLayer(ipL);
}
MOLE graphics can also be managed using display lists (IDisplayList). Display lists are collections of objects that are stored in memory as caches. You can use display lists when low-level control is needed, such as when using MOLE's decluttering (grouping) and callback capabilities. The display list handles the relationships between cached graphic objects and how they appear in a display, such as how graphics are stacked or leadered. All MOLE graphic types (force elements, tactical graphics, leaders, and stacks) can be managed using display lists.
Display lists currently work in two dimensions (2D) only.
The following diagram shows the members of the IDisplayList interface:
The Add and Remove methods allow you to add and remove cached graphic objects to and from the display list as needed. Call the Refresh method after adding or removing objects from the display list. Call the Draw method to draw the contents of the display list to a display.
To use display lists, create MOLE cached graphic objects (FEGraphic or TacticalGraphic types), add them to a ForceElementDisplayList or TacticalGraphicDisplayList, hook into the IActiveViewEvents.AfterDraw event, and render the ForceElementDisplayList or TacticalGraphicDisplayList with a single call to the Draw method.
The following code example shows how to draw MOLE graphics on a map using display lists:
[VB.NET]
Private Sub btnCreateDisplayList_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnCreateDisplayList.Click
' Create the display list.
m_MoleDisplayList = New ForceElementDisplayListClass()
Dim fedl As IForceElementDisplayList = TryCast(m_MoleDisplayList, IForceElementDisplayList)
' Create and add an FE graphic to the display list.
m_MoleDisplayList.Add(BuildFEG(Nothing, -97.83, 31, DEFAULT_SIC, DEFAULT_NAME, DEFAULT_PARENT))
' Set the size based on the map extent.
Dim Size As Double = m_ActiveView.Extent.Height * 0.075
fedl.Size = Size
' Partially refresh the map view.
m_ActiveView.PartialRefresh(esriViewDrawPhase.esriViewForeground, Type.Missing, m_ActiveView.Extent)
End Sub
[C#]
private void btnCreateDisplayList_Click(object sender, EventArgs e)
{
// Create the display list.
m_MoleDisplayList = new ForceElementDisplayListClass();
IForceElementDisplayList fedl = m_MoleDisplayList as IForceElementDisplayList;
// Create and add an FE graphic to the display list.
m_MoleDisplayList.Add(BuildFEG(null, - 97.83, 31, DEFAULT_SIC, DEFAULT_NAME,
DEFAULT_PARENT));
// Set the size based on the map extent.
double size = m_ActiveView.Extent.Height * 0.075;
fedl.Size = size;
// Partially refresh the map view.
m_ActiveView.PartialRefresh(esriViewDrawPhase.esriViewForeground, Type.Missing,
m_ActiveView.Extent);
}
Display lists and MOLE feature layers
Display lists are also contained in MOLE feature layers via the ICachedGraphicFeatureLayer interface. This design gives MOLE feature layers decluttering (leadering/stacking) capability as well. Both the ForceElementLayer and the TacticalGraphicLayer classes implement this interface. In this capacity, MOLE graphics are stored in the display list and rendered on a map as cached graphic objects (FEGraphic or TacticalGraphic types). The following diagram shows the members of the ICachedGraphicFeatureLayer interface:
Graphic groups
MOLE provides many different ways to modify the appearance of force element graphics and graphics within layers, one of which involves grouping graphics into various arrangements, referred to as graphic groups. A graphic group is a technique for spatially organizing graphics in the map display. Graphic groups use rules and styles to determine when graphics are grouped and how graphics appear when grouped. Display lists are used to manage graphic groups. Graphic groups consist of two primary types, the graphic group leader and the stack. When working with leaders and stacks, rules are associated with both and used to determine what gets leadered and/or stacked.
Force element graphics can be arranged into groups using grouping rules, which determine when grouping occurs and the grouping behavior. Grouping rule classes implement IAutoGroupRule. All leader and stack rules implement the IAutoGroupRule interface. Functions on this interface include count, add, remove, and clear. The sole difference between leaders and stacks is that leaders have styles. Stack and leader rules are persisted in a MOLE layer.
Leadering
Leadering is a useful method to organize and declutter graphics in the display. Leadering is a type of force element group that groups symbols together on leader lines based on predefined leadering rules. For example, symbols with a common major subordinate command (higher formation or parent) can be grouped together.
GraphicLeader is a cached graphic type that implements IGraphicLeader. Using IGraphicLeader, you can get a geometry bag that contains the shapes that make up the leader. The end of a leader is a two-point line; the from point cannot be changed but is calculated for you as the average location of graphics grouped on a leader. The to point or elbow location can be moved to reposition the leader.
Details of the GraphicLeader class are shown in the following diagram:
Leader styles
Leaders use styles to manage their organizational structure and appearance. A leader style object is an object of the GraphicLeaderStyle class and implements the IGraphicLeaderStyle interface. The leader style object determines the leader style used when creating a graphic leader object.
While a graphic leader defines what is being leadered, the graphic leader style defines how the leadered objects are visualized. A force element display list can be assigned a default leader style through its ILeaderingProperties interface. This default style is attached to any new graphic leader object created by the display list's leadering logic.
When a graphic leader object is drawn or refreshed, the IGraphicLeaderStyle interface is used to determine the physical appearance and layout of that leader object as well as the display properties of the graphic leader object, and optionally, to manage the order and appearance of its respective cached graphic members.
Details of the GraphicLeaderStyle class are shown in the following diagram:
Each graphic leader style can define a single marker, line, and/or fill symbol that's used by the leader to symbolize the geometric shapes that define its structure. Some leader styles also implement the ICustomProperties interface. This interface enables clients to access properties specific to a given style, without the need for a style class-specific interface. A style that implements ICustomProperties uses those properties to control characteristics unique to that particular style.
Leadering rules
An example of the graphic group rules is the LeaderingRule class. MOLE provides the following four leadering rule classes that implement ILeaderingRule and IAutoGroupRule:
These leader rule classes can be used to determine if two leaderable graphics should be leadered together.
A LeaderingRule object answers the question, "Given two cached graphics with point geometries (that is, two force elements), should these objects be leadered together?" Different leader rules evaluate different properties of the graphics in question and/or their attached features or force elements to determine the ShouldBeLeadered method value as true or false.
Leader rules are created and added to a force element display list's leader rule set through that display list's ILeaderingProperties interface. Rules added to a display list in this way are automatically evaluated each time a new force element graphic is added to the display list, an existing graphic is altered, or other events occur that alter the leadering environment of the display list. You also have the option of taking a given leadering rule and explicitly applying its "should be leadered" logic using any two leaderable cached graphics as arguments.
The following code example demonstrates how to create a new leader object, add two graphics to it, and draw it to a display:
[VB.NET]
' Create a graphic leader and set up its style.
Dim leader As IGraphicLeader = New GraphicLeaderClass()
Dim cached As ICachedGraphic = TryCast(leader, ICachedGraphic)
Dim style As IGraphicLeaderStyle = New BracketLeaderStyleClass()
leader.Style = style
' Use a default size for the leader.
Sizify(cached)
' Build and immediately refresh two FE graphics.
Dim fe1 As ICachedGraphic = BuildFEG(Nothing, -97.5, 31.31, DEFAULT_SIC, DEFAULT_NAME, DEFAULT_PARENT)
Dim fe2 As ICachedGraphic = BuildFEG(Nothing, -97.5, 31.31, DEFAULT_SIC, DEFAULT_NAME, DEFAULT_PARENT)
fe1.Refresh(m_ActiveView.ScreenDisplay)
fe2.Refresh(m_ActiveView.ScreenDisplay)
' Add the graphics to the stack.
leader.Add(TryCast(fe1, ILeaderable))
leader.Add(TryCast(fe2, ILeaderable))
' Add the leader to the map.
AddToMap(cached)
[C#]
// Create a graphic leader and set up its style.
IGraphicLeader leader = new GraphicLeaderClass();
ICachedGraphic cached = leader as ICachedGraphic;
IGraphicLeaderStyle style = new BracketLeaderStyleClass();
leader.Style = style;
// Use a default size for the leader.
Sizify(cached);
// Build and immediately refresh two FE graphics.
ICachedGraphic fe1 = BuildFEG(null, - 97.5, 31.31, DEFAULT_SIC, DEFAULT_NAME,
DEFAULT_PARENT);
ICachedGraphic fe2 = BuildFEG(null, - 97.5, 31.31, DEFAULT_SIC, DEFAULT_NAME,
DEFAULT_PARENT);
fe1.Refresh(m_ActiveView.ScreenDisplay);
fe2.Refresh(m_ActiveView.ScreenDisplay);
// Add the graphics to the stack.
leader.Add(fe1 as ILeaderable);
leader.Add(fe2 as ILeaderable);
// Add the leader to the map.
AddToMap(cached);
Stacking
Force elements that are of similar type can be drawn together in stacks. You can use the IGraphicStack interface to group two or more objects so that they are drawn overlapping each other, thus serving as a decluttering technique. A graphic stack is an ordered collection of stackable cached graphics, drawn to resemble a stack of cards. The top graphic in a stack is visible. You can use a force element display list's IStackingProperties interface to control the members that manage the force element graphic stacking environment.
IsStackingEnabled toggles the stacking environment on and off. ShouldDrawStackHull indicates whether hulls should be drawn around the locations of stacked graphics. Use the StackHullBuffer property to define the buffer distance hulls used with stacked graphics. Set the StackHullSymbol property to define the fill symbol used to symbolize hulls drawn around the locations of stacked graphics. The StackRuleSet property is a set of auto group rules used to determine what should be stacked. The following illustration shows examples of stacked graphics:
Use the IGraphicStack interface of a given GraphicStack object to manage a group of cached graphics as a stack. StackingRules, the classes that implement IStackingRule, as well as IAutoGroupRule, can call on ShouldBeStacked and IsEnabled, respectively, to determine the stackability of any two force element graphics. Use the IGraphicStack interface to parse through the collection of cached graphics to access them as necessary.
Details of the GraphicStack class are shown in the following diagram:
The following code example demonstrates how to create a new stack object, add two graphics to it, and draw it to a display:
[VB.NET]
' Create a graphic stack.
Dim stack As IGraphicStack = New GraphicStackClass()
Dim cached As ICachedGraphic = TryCast(stack, ICachedGraphic)
' Use a default size for the stack.
Sizify(cached)
' Build and immediately refresh two FE graphics.
Dim fe1 As ICachedGraphic = BuildFEG(Nothing, -97.5, 31, DEFAULT_SIC, DEFAULT_NAME, DEFAULT_PARENT)
Dim fe2 As ICachedGraphic = BuildFEG(Nothing, -97.5, 31, DEFAULT_SIC, DEFAULT_NAME, DEFAULT_PARENT)
fe1.Refresh(m_ActiveView.ScreenDisplay)
fe2.Refresh(m_ActiveView.ScreenDisplay)
' Add the graphics to the stack.
stack.Add(TryCast(fe1, IStackable))
stack.Add(TryCast(fe2, IStackable))
' Add the stack to the map.
AddToMap(cached)
[C#]
// Create a graphic stack.
IGraphicStack stack = new GraphicStackClass();
ICachedGraphic cached = stack as ICachedGraphic;
// Use a default size for the stack.
Sizify(cached);
// Build and immediately refresh two FE graphics.
ICachedGraphic fe1 = BuildFEG(null, - 97.5, 31, DEFAULT_SIC, DEFAULT_NAME,
DEFAULT_PARENT);
ICachedGraphic fe2 = BuildFEG(null, - 97.5, 31, DEFAULT_SIC, DEFAULT_NAME,
DEFAULT_PARENT);
fe1.Refresh(m_ActiveView.ScreenDisplay);
fe2.Refresh(m_ActiveView.ScreenDisplay);
// Add the graphics to the stack.
stack.Add(fe1 as IStackable);
stack.Add(fe2 as IStackable);
// Add the stack to the map.
AddToMap(cached);
If you want the lowest level of control over MOLE symbols, you can manage them as individual graphic objects, using the cached graphic method. This approach allows you to achieve high speed graphic rendering performance, which is required by most applications that feature dynamic or real-time updates in a display. This level of performance is possible because MOLE graphic objects are stored in memory, rather than in feature layers. This design avoids the overhead of accessing features stored on disk in geodatabases. However, this approach also requires the most amount of development effort, and you can't take advantage of prepackaged functionality, such as leader and stack rules.
ICachedGraphic
The ICachedGraphic interface controls members that are common to all cached graphic objects, such as a force element cached graphic or a tactical cached graphic. The following diagram shows the members of this interface:
The Size property is specified in map units. The geometry determines the geographic location of the graphic. An FEGraphic cached graphic can be rotated by setting the Angle property to a value in radians.
When drawing a cached graphic to a display, if the IsDirty property evaluates to true, it triggers a reconstruction of the graphic. This is the same operation that occurs when an explicit refresh on that graphic is called. When various properties of a cached graphic are changed, that graphic's IsDirty property is set to true. Cached graphics retain pointers to the appropriate objects that facilitate their reconstruction when reconstruction becomes necessary.
The ICachedGraphic2 interface supports the same members as ICachedGraphic but also makes it possible to control a graphic's visibility via the IsVisible property.
MOLE keeps a copy of the 15-character string internally, so you can retrieve it each time without having to go to the source primitive features used for constructing an FEGraphic.
To use the cached graphics method to render MOLE symbols, you need to be familiar with MOLE graphic renderers—objects that manage the various combinations of a Symbol ID code and geometry type into a MIL-STD-2525B Change 2 or APP-6A compliant graphic.
MOLE graphic renderers
MOLE uses a series of custom graphic renderers to render MIL-STD-2525B Change 2 and APP-6A symbols in a display. Some of the more commonly used graphic renderer classes are ForceElement2525BRenderer and ForceElementAPP6ARenderer. At the lowest level, all MOLE graphic renderer classes implement a common interface: ICacheRenderer. This interface provides the primary mechanism used in MOLE for rendering force element and tactical graphics. The following diagram shows the members of the ICacheRenderer interface:
ICacheRenderer exposes the following three public methods:
These methods are also called internally to render MOLE graphics using feature classes. GraphicByFeature renders a feature as a cached graphic and returns it to the caller. RenderCursor loops through multiple features, rendering each feature in each iteration. RenderFeature renders one feature at a time. Internally, RenderFeature calls the GraphicByFeature method.
RenderCursor uses a FeatureCursor object to populate a display list with one MOLE graphic for each feature it encounters. For example, the RenderCursor method can render a subset of features, such as when it has been given the query results of an ArcObjects function call to a subset of features. This is useful when rendering a subgroup of symbols, such as a group of force elements with a specific attribute or unit type—for example, infantry ground units.
Rendering force element graphics
MOLE uses the following basic steps to construct and render a force element symbol using the Symbol ID code:
- Parse the 15-character Symbol ID code and decodes it into a collection of message strings—each corresponding to specific characters and combinations of characters in the Symbol ID code.
- Use a series of configuration tables to determine the appropriate symbol pieces or graphic components needed.
- Retrieve the symbol pieces or graphic components from a library of line and polygon geometric primitive components (originally derived from computer graphic metafiles). When merged, these primitive components constitute the complete MOLE MIL-STD-2525B Change 2 or APP-6A graphic.
- Draw the MOLE force element graphic to a given display object using the graphic's attached style.
The following left and center illustrations show all line and polygon geometric primitives to illustrate how many primitives there are and how many possible combinations there can be. The illustration on the right shows the primitives required to construct a naval combat infantry ground unit, including frame components used for all symbol affiliations.
The ForceElement2525BRenderer and ForceElementAPP6ARenderer classes are the only default renderers used in the construction of force element graphics. Use the ForceElement2525BRenderer class when you want to render any MIL-STD-2525B Change 2 force element graphic. Use the ForceElementAPP6ARenderer class to render any APP-6A force element graphic. The following code example shows how to create a force element cached graphic using the ForceElement2525BRenderer class:
[VB.NET]
Private Function BuildFEG(ByVal style As IFEGraphicStyle, ByVal x As Double, ByVal y As Double, ByVal SIC As String, ByVal Name As String, ByVal parent As String) As ICachedGraphic
' Verify SIC length.
If SIC.Length <> 15 Then
SIC = "SFGP-----------"
End If
' Create the point.
Dim pint As IPoint = New PointClass()
pint.PutCoords(x, y)
' Create a force element.
Dim felement As IForceElement = New ForceElementClass()
felement.MessageString = SIC
felement.PropertySet.SetProperty("Name", Name)
felement.PropertySet.SetProperty("Parent", parent)
felement.Shape = pint
' Get a force element renderer.
Dim helper As IMoleCoreHelper = New MoleCoreHelperClass()
m_FERenderer = helper.ForceElementRenderer
' Create an FE graphic.
Dim fegraphic As IFEGraphic = TryCast((TryCast(m_FERenderer, IForceElement2525BRenderer)).GraphicByForceElement(felement), IFEGraphic)
' Attach the force element to the graphic.
fegraphic.ForceElement = felement
' Attach the style if nonnull.
If Not style Is Nothing Then
fegraphic.Style = style
End If
Return TryCast(fegraphic, ICachedGraphic)
End Function
[C#]
private ICachedGraphic BuildFEG(IFEGraphicStyle style, double x, double y, string
SIC, string name, string parent)
{
// Verify SIC length.
if (SIC.Length != 15)
SIC = "SFGP-----------";
// Create the point.
IPoint pint = new PointClass();
pint.PutCoords(x, y);
// Create a force element.
IForceElement felement = new ForceElementClass();
felement.MessageString = SIC;
felement.PropertySet.SetProperty("Name", name);
felement.PropertySet.SetProperty("Parent", parent);
felement.Shape = pint;
// Get a force element renderer.
IMoleCoreHelper helper = new MoleCoreHelperClass();
m_FERenderer = helper.ForceElementRenderer;
// Create an FE graphic.
IFEGraphic fegraphic = (m_FERenderer as IForceElement2525BRenderer)
.GraphicByForceElement(felement)as IFEGraphic;
// Attach the force element to the graphic.
fegraphic.ForceElement = felement;
// Attach the style if nonnull.
if (style != null)
fegraphic.Style = style;
return fegraphic as ICachedGraphic;
}
[VCPP]
// Illustrates use of the ICacheRenderer and ForceElement2525BRenderer in creating a force element cached graphic object
ICacheRendererPtr m_ipRenderer;
void TestAppDlg::RenderForceElementFeature(IFeaturePtr ipFeature,
IActiveViewPtr ipActiveView)
{
if (m_ipRenderer == NULL)
m_ipRenderer.CreateInstance(CLSID_ForceElement2525BRenderer);
VARIANT_BOOL canRender;
m_ipRenderer->CanRenderFeature(ipFeature, &canRender);
if (canRender == VARIANT_TRUE)
{
// get the active view's display
IScreenDisplayPtr ipScreenDisplay;
ipActiveView->get_ScreenDisplay(&ipScreenDisplay);
// get the active view’s extent height
IEnvelopePtr ipExtent;
ipActiveView->get_Extent(&ipExtent);
double height;
ipExtent->get_Height(&height);
// get the graphic for the feature
ICachedGraphicPtr ipGraphic;
m_ipRenderer->GraphicByFeature(ipScreenDisplay, ipFeature, &ipGraphic);
// set the graphic size
ipGraphic->put_Size(0.05 *height);
// draw the graphic
long hDC;
ipScreenDisplay->get_hDC(&hDC);
ipScreenDisplay->StartDrawing(hDC, 0);
ipGraphic->Draw(ipScreenDisplay, NULL);
ipScreenDisplay->FinishDrawing();
}
}
In the previous code example, the GraphicByForceElement method generates a graphic using the ForceElement object, rather than using a feature (such as when calling the ICacheRenderer.GraphicByFeature method). The ForceElement object acts as a stand-in for a feature, providing the geometry, symbol ID, and attribute information (contained within a property set) that a feature would otherwise provide, and it can be used to create a MIL-STD-2525B Change 2 or APP-6A graphic internally in the absence of a feature. In the absence of a feature, the ForceElement object acts as a pseudo feature.
FEGraphic
FEGraphic is a type of cached graphic that is used to symbolize a force element. The FEGraphic class implements the IFEGraphic interface. You can associate an FEGraphic object with a feature using the ICacheRenderer.GraphicByFeature method. The following diagram shows the interfaces implemented by the FEGraphic class:
An FEGraphic can exist in the following three states:
- By itself—A 15-character code and the x,y coordinates are needed
- Attached to a feature—FEGraphic sees the attributes to set how to draw itself
- Attached to anything that implements the IForceElement interface
IFEGraphic
One of the key interfaces implemented by the FEGraphic class is IFEGraphic, and it is shown in the following diagram:
Several low-level elements exist under the IFEGraphic interface. Though not typically used by developers, these elements describe the internal concepts of MOLE and are therefore pertinent for development. For example, EchelonGraphicComponent, FrameGraphicComponent, IconGraphicComponent, and MobilityGraphicComponent are read only and are the underlying objects comprising a collection of vectors that a particular graphic needs to draw itself, resulting from what the FEGraphicFactory has generated using the symbol ID.
FEGraphicFactory populates the four components for a given graphic. These are available as read-only properties—do not create an instance of IconGraphicComponent and attempt to attach it. With this read-only behavior, the elements are exposed, and you can look at the geometry primitives that make up the mobility in order to change geometry, make it bolder, and so on. FrameArea is the polygon that is used to make up the symbol frame. FrameOutlineSymbol is read only and is used to draw the outside of a frame.
The message string is an expanded version of the symbol ID and is read only. Therefore, FEGraphic can be changed by altering its message string. The IFEGraphic.ForceElement property allows FEGraphic to be permanently attached to any object implementing IForceElement. This allows FEGraphic to be alone, with no attachments to another parent. Thus, it uses its own symbol ID (message string) and its geometry via the ICachedGraphic interface to define what it looks like and can be updated using refresh or draw calls. If it has a feature, then the feature will be attached via the IFeatureGraphic interface. Or, if it has a force element, it will be attached using the IForceElement interface (IFEGraphic) through the ForceElement property.
When FEGraphic is refreshed, MOLE looks for an attached feature. If it finds an attached feature, it goes to the feature to obtain geometry, the 15-character Symbol ID code, and other text attributes using these elements to perform an Attach and construct itself. If no feature is attached via this property, MOLE looks for a force element object—any object implementing the IForceElement interface. At this point, you can implement the IForceElement interface to symbolize an object. For example, if a feature is used but fails to provide a valid 15-character Symbol ID string (for example, less than or greater than 15 characters), MOLE renders the feature as unknown or with a question mark. If it is given a force element and feature that does not have a symbol ID, or a feature with a Symbol ID code that is a 14-character string (or another non-15-character string), MOLE does not record errors but simply draws a question mark. MOLE does not have an internal error reporting mechanism for improper symbol IDs.
FEGraphicFactory
The force element graphic factory (FEGraphicFactory class) is the object that provides access to the internal mechanism for building MOLE graphics from basic information. The following diagram shows the details of the FEGraphicFactory class:
The FEGraphicFactory object decodes the Symbol ID code using the DecodeMessageString method. The message string is a local copy of the string. FEGraphicFactory holds a pointer to the object that built it (the pointer back to the FEGraphicFactory that generated it). It uses this pointer to perform internal functions, such as decoding and encoding. DecodeMessageString and EncodeMessageString are utility functions for breaking apart and recombining 15-character strings. The MOLE message string acts as a parsing function—taking a string and producing a structure, then changing a structure back into a 15-character string using a reverse function (EncodeMessageString).
Use the Attach method to update the symbol ID for an existing FEGraphic. For example, an existing graphic of type infantry battalion has a corresponding 15-character string of SSGAUCIZ---F--G representing a suspect, anticipated object. Given the graphic changes to a present, hostile status, SHGPUCIZ---F--G, the second character changes from S to H and the fourth character from A to P. Using the new Symbol ID code, use Attach to update an existing FEGraphic with the new string. This use of the Make property performs a graphic update and prevents having to construct another graphic, as well as saves time by not having to use the factory to make the graphic object.
The FEGraphicFactory object also implements the IFactoryTooling interface, which can be configured to interpret message strings and produce FEGraphic objects compliant with either supported military symbology standard (MIL-STD-2525B Change 2 or APP-6A). ForceElement2525BRenderer uses FEGraphicFactory to produce an FEGraphic object.
FEGraphicStyle
The two MOLE force element renderers (ForceElement2525BRenderer and ForceElementAPP6ARenderer) use a default force element graphic style (FEGraphicStyle class) to define or modify the appearance of newly created MIL-STD-2525B Change 2 or APP-6A compliant symbols. FEGraphicStyle controls how the symbol appears—what color it is and if it is filled or not, for example. You can create new symbol properties for use when applying a style. For example, a new FEGraphicStyle could be black and white, rather than the standard symbol affiliation colors. The following diagram shows the details of this class:
The following code example shows how to use the FEGraphicStyle class:
[VB.NET]
' Create a color.
Dim RGB As IRgbColor = New RgbColorClass()
RGB.Blue = 200
' Create a fill symbol.
Dim sfs As ISimpleFillSymbol = New SimpleFillSymbolClass()
sfs.Color = RGB
' Create a font.
Dim font As IFontDisp = ESRI.ArcGIS.ADF.Converter.ToStdFont(New System.Drawing.Font("Courier New", 20, System.Drawing.FontStyle.Bold))
' Create the text symbol.
Dim textsymbol As ITextSymbol = New TextSymbolClass()
textsymbol.Font = font
' Create an FE graphic style.
Dim style As IFEGraphicStyle = New FEGraphicStyleClass()
style.Name = "My New Style"
style.UseFonts = True
style.TextSymbol = textsymbol
style.FriendlyPresentFrameFill = TryCast(sfs, IFillSymbol)
[C#]
// Create a color.
IRgbColor rgb = new RgbColorClass();
rgb.Blue = 200;
// Create a fill symbol.
ISimpleFillSymbol sfs = new SimpleFillSymbolClass();
sfs.Color = rgb;
// Create a font.
IFontDisp font = new StdFontClass()as IFontDisp;
font.Name = "Courier New";
font.Bold = true;
// Create the text symbol.
ITextSymbol textsymbol = new TextSymbolClass();
textsymbol.Font = font;
// Create an FE graphic style.
IFEGraphicStyle style = new FEGraphicStyleClass();
style.Name = "My New Style";
style.UseFonts = true;
style.TextSymbol = textsymbol;
style.FriendlyPresentFrameFill = sfs as IFillSymbol;
An FEGraphicStyle, once associated with a MOLE renderer, becomes the default style used in the construction of any graphic created from that point forward, thereby determining the construction and display characteristics of the symbols. For example, when creating a new instance of ForceElement2525BRenderer, the default style used is the 2525B standard style, which is used for constructing all force element symbols thereafter. If you want to create your own style, you can modify the 2525B style or define a new style and apply it when creating your own instance of the style.
Rendering tactical graphics
MOLE provides a set of renderer objects that can accept input information and return tactical graphic objects. All of these renderers derive from the TacticalGraphicRenderer class. Use these TacticalGraphicRenderer classes when you want to render MIL-STD-2525B and APP-6A tactical graphic symbols.
Tactical graphic symbols are rendered using the following elements:
- Geometry
- Symbol ID code
- Any tactical graphic renderer
Each TacticalGraphicRenderer object is responsible for rendering a set of tactical graphics. Each tactical graphic has the capability of providing information to a developer (through ArcMap or a developer's application) about which MIL-STD-2525B or APP-6A graphic it knows how to construct and render.
The basic concept behind MOLE's use of the Symbol ID code to render tactical graphics is as follows: given a feature attribute and input geometry, use the appropriate TacticalGraphicRenderer to render the feature. MOLE also uses ArcGIS style sheets to build some of the more complex tactical graphic symbols, such as Forward Line of Own Troops (FLOT) lines. If the feature does not conform to these requirements, it is displayed in magenta as a simple line feature without MOLE symbology.
All TacticalGraphicRenderer types implement the following interfaces: ITacticalGraphicRenderer, IConstructTacticalElement, and IGraphicDefSet. Use the ITacticalGraphicRenderer interface to set the text size for attribute information displayed with tactical graphics, as well as for rendering a symbol using the affiliation (second character of the Symbol ID code) color of the graphic with the UseAffiliationColor property. Tactical graphic renderers can be created to incorporate custom rendering capabilities within MOLE. You can use custom renderers in conjunction with, or in place of, other renderers. Once created, a custom renderer must be registered with the MOLE configuration database via its TacticalGraphicRenderers table. The following diagram shows the details of the ITacticalGraphicRenderer interface:
Use the IGraphicDefSet interface to return a set of GraphicDef objects. Each GraphicDef object provides a definition about one particular MIL-STD-2525B or APP-6A symbol handled by the given renderer. The following diagram shows the details of the IGraphicDefSet interface:
The GraphicDef class can be used to return the following information about a tactical graphic renderer:
- The name
- How to build the core geometry for the graphic definition
- The class identifier (CLSID) of the MOLE feature class extension
- The function code component of the Symbol ID code (character positions 5—10)
The following diagram shows the details of the GraphicDef class:
The IGeometryLimits interface of each GraphicDef object can be used to return information about the type of geometry that must be provided to support the rendering of a graphic in compliance with each GraphicDef and any vertex count limits that must be respected in any geometry used to render a graphic in compliance with each GraphicDef.
TacticalGraphic
TacticalGraphic is a cached graphic type that symbolizes a point-, line-, or area-based tactical military object, using either a feature or tactical element. TacticalGraphic objects are created using any of the tactical graphic renderers. The following diagram shows the interfaces implemented by this class:
TacticalGraphic implements ITacticalGraphic and ITacticalGraphic2. These interfaces are identical, except that ITacticalGraphic2 has a SymbolID member that returns the tactical graphic's Symbol ID code string. The following diagram shows the details of the ITacticalGraphic and ITacticalGraphic2 interfaces:
Rendering tactical graphic lines
There are many tactical graphic line symbols in MIL-STD-2525B Change 2 and APP-6A, each with defined geometry types and properties for constructing them. MOLE uses multiple renderers to handle these different cases. The renderers and the symbols they support are accessible using the GraphicDef class IGraphicDef interface.
Arrow symbols can be either straight (three points) or curved (multipoint), depending on the type of arrow. Airborne, aviation, and rotary wing axis of advance arrows cannot be curved; all others can be straight or curved. When creating arrow symbols, the location and positioning of the vertices, except for the last one placed, determine the shape and length of the arrow body.
In the following illustration, the position of the last vertex defines the shape and size of the arrowhead. The red point represents the last vertex placed; as illustrated, the last vertex must be placed behind the previous vertex for MOLE to render the arrowhead. Tactical graphic obstacle features (points, lines, and areas) can also be displayed in green, as specified by MIL-STD-2525B Change 2 and APP-6A. This is controlled by an additional renderer that can be activated from the Layer Properties dialog box.
Rendering tactical graphic areas and points
Area and point tactical graphic rendering is straightforward compared to lines. The only instance deserving special attention is when minefield symbols are displayed. As with lines, if MOLE encounters area and point symbols with an invalid symbol ID or a symbol ID for which MOLE has no renderer, those symbols are displayed in magenta as simple area or point features without MOLE symbology. See the following graphics:
TacticalElement
TacticalElement is a lightweight class that stores attribute information that can be symbolized by a tactical graphic. It is similar to the ForceElement class and IForceElement interface in that it can also act as a pseudo feature. The TacticalElement class implements the ITacticalElement interface, which controls members that enable an object to provide attribute information for a graphic to symbolize. The following diagram shows the details of the TacticalElement class:
The IConstructTacticalElement interface can be used to build and return a representative tactical element and is shown in the following diagram:
The following code example shows how to use the TacticalElement class to retrieve a representative graphic and draw it:
[VB.NET]
' Get the first renderer.
Dim renderer As ICacheRenderer = TryCast(m_TGRenderers.Element(0), ICacheRenderer)
' Create a point.
Dim pint As IPoint = New PointClass()
pint.PutCoords( -97.83, 31.12)
' Get the TG renderer's graphic definition.
Dim graphdefs As IGraphicDefSet = TryCast(renderer, IGraphicDefSet)
Dim defset As ISet = graphdefs.GraphicDefSet
defset.Reset()
Dim def As IGraphicDef = TryCast(defset.Next(), IGraphicDef)
' Construct a tactical element.
Dim constructor As IConstructTacticalElement = TryCast(renderer, IConstructTacticalElement)
Dim tacelement As ITacticalElement = constructor.ReturnSampleElement(mole2525BAffiliationsEnum.moleAffiliationFriend, mole2525BEchelonsEnum.moleEchelonDivision, def, m_ActiveView.Extent, pint, False)
' Determine if the returned element can be rendered.
Dim tgr As ITacticalGraphicRenderer = TryCast(renderer, ITacticalGraphicRenderer)
If tgr.CanRenderTacticalElement(tacelement) Then
' Set the renderer's size and text size based on the map extent.
Dim Size As Double = m_ActiveView.Extent.Height * 0.01
Dim tacticalGraphicsRenderer As ITacticalGraphicRenderer2 = TryCast(tgr, ITacticalGraphicRenderer2)
tacticalGraphicsRenderer.Size = Size
Dim textsize As Double = m_ActiveView.Extent.Height * 0.01
tgr.TextSize = textsize
' Create the graphic.
Dim cached As ICachedGraphic = tgr.GraphicByTacticalElement(m_ActiveView.ScreenDisplay, tacelement)
' Add the graphic to the map.
AddToMap(cached)
End If
[C#]
// Get the first renderer.
ICacheRenderer renderer = m_TGRenderers.get_Element(0)as ICacheRenderer;
// Create a point.
IPoint pint = new PointClass();
pint.PutCoords( - 97.83, 31.12);
// Get the TG renderer's graphic definition.
IGraphicDefSet graphdefs = renderer as IGraphicDefSet;
ISet defset = graphdefs.GraphicDefSet;
defset.Reset();
IGraphicDef def = defset.Next()as IGraphicDef;
// Construct a tactical element.
IConstructTacticalElement constructor = renderer as IConstructTacticalElement;
ITacticalElement tacelement = constructor.ReturnSampleElement
(mole2525BAffiliationsEnum.moleAffiliationFriend,
mole2525BEchelonsEnum.moleEchelonDivision, def, m_Extent, pint, false);
// Determine if the returned element can be rendered.
ITacticalGraphicRenderer tgr = renderer as ITacticalGraphicRenderer;
if (tgr.CanRenderTacticalElement(tacelement))
{
// Set the renderer's size and text size based on the map extent.
double size = m_ActiveView.Extent.Height * 0.01;
(tgr as ITacticalGraphicRenderer2).Size = size;
double textsize = m_ActiveView.Extent.Height * 0.01;
tgr.TextSize = textsize;
// Create the graphic.
ICachedGraphic cached = tgr.GraphicByTacticalElement(m_ActiveView.ScreenDisplay,
tacelement);
// Add the graphic to the map.
AddToMap(cached);
}
[VCPP]
// Illustrates using a TacticalElement to retrieve a representative graphic, and then drawing it
// NOTE: m_ipTGRenderer is an ICacheRenderer member variable
LRESULT TestAppDlg::OnGenerateTGClicked(WORD wNotifyCode, WORD wID, HWND
hWndCtl, BOOL &bHandled)
{
// m_ipActiveView set prior to here
if (m_ipActiveView == NULL)
return 0;
IScreenDisplayPtr ipScreenDisplay;
m_ipActiveView->get_ScreenDisplay(&ipScreenDisplay);
IEnvelopePtr ipExtent;
m_ipActiveView->get_Extent(&ipExtent);
double height;
ipExtent->get_Height(&height);
// get the graphic definition
IGraphicDefSetPtr ipGraphicDefinitions(m_ipTGRenderer);
ISetPtr ipDefinitionSet;
ipGraphicDefinitions->get_GraphicDefSet(&ipDefinitionSet);
ipDefinitionSet->Reset();
IUnknownPtr ipUnk;
ipDefinitionSet->Next(&ipUnk);
IGraphicDefPtr ipGraphicDef(ipUnk);
// create a point
IPointPtr ipPoint(CLSID_Point);
ipPoint->PutCoords(0, 0);
// contruct a sample tactical element
IConstructTacticalElementPtr ipConstructElement(m_ipTGRenderer);
ITacticalElementPtr ipTacticalElement;
ipConstructElement->ReturnSampleElement(moleAffiliationFriend,
moleEchelonDivision, ipGraphicDef, ipPoint, VARIANT_FALSE,
&ipTacticalElement);
// if possible, build a tactical graphic
ITacticalGraphicRendererPtr ipTGR(m_ipTGRenderer);
VARIANT_BOOL candoit;
ipTGR->CanRenderTacticalElement(ipTacticalElement, &candoit);
if (candoit == VARIANT_TRUE)
{
// set the text size
ipTGR->put_TextSize(0.05 *height);
// generate the graphic
ICachedGraphicPtr ipGraphic;
ipTGR->GraphicByTacticalElement(ipScreenDisplay, ipTacticalElement,
&ipGraphic);
// draw the graphic
long hDC;
ipScreenDisplay->get_hDC(&hDC);
ipScreenDisplay->StartDrawing(hDC, 0);
ipGraphic->Draw(ipScreenDisplay, NULL);
ipScreenDisplay->FinishDrawing();
}
return 0;
}
Drawing MOLE cached graphics
In general, you must have access to a display object to render MIL-STD-2525B Change 2 and APP-6A graphics. MOLE uses a display list object as its own custom feature layer container to manage persistent graphic objects in a display. ArcObjects provides the means of drawing geospatial information (feature layers in the geodatabase object) to a display object implementing IDisplay. In ArcObjects, the IDisplay interface is simply an interface to draw points, lines, polygons, rectangles, and text on a display device. In addition, you can use or develop your own business objects that have the ability to draw to IDisplay. Therefore, MOLE can render and draw MIL-STD-2525B Change 2 and APP-6A symbology to any object implementing IDisplay.
Exporting MOLE cached graphics
Exporting MOLE cached graphics to a device-independent bitmap (DIB) can be achieved using the GraphicExporter class. Use the IGraphicExporter interface to accumulate a collection of exportable graphics and export them all to a single output file or DIB. Use the IExportGraphic.ExportToFile method to export a single graphic to an image file (bitmap [.bmp], Portable Network Graphics [.png], Graphic Interchange Format [.gif], tagged image file [.tif], or Joint Photographics Experts Group [.jpg]).
The following diagram shows the details of the GraphicExporter class:
The following code example shows how to use the IExportGraphic interface to export a single graphic to a Graphic Interchange Format (GIF) image:
[VB.NET]
' Create the exportable sample graphic.
Dim exporter As IExportGraphic = TryCast(BuildFEG(Nothing, 1, 1, DEFAULT_SIC, DEFAULT_NAME, DEFAULT_PARENT), IExportGraphic)
' Refresh the graphic (to set its correct bounds).
Dim cachedGraphic As ICachedGraphic = TryCast(exporter, ICachedGraphic)
cachedGraphic.Refresh(m_ActiveView.ScreenDisplay)
' Get the graphic's extent.
Dim ext As IEnvelope = New EnvelopeClass()
Dim cachedGraphics As ICachedGraphic = TryCast(exporter, ICachedGraphic)
cachedGraphics.QueryBounds(m_ActiveView.ScreenDisplay, ext)
' Export the image.
Exporter.ExportToFile(m_ActiveView.ScreenDisplay, filepath, ext, 128, 128, 128, 1.0, 128, False)
End If
[C#]
// Create the exportable sample graphic.
IExportGraphic exporter = BuildFEG(null, 1, 1, DEFAULT_SIC, DEFAULT_NAME,
DEFAULT_PARENT)as IExportGraphic;
// Refresh the graphic (to set its correct bounds).
(exporter as ICachedGraphic).Refresh(m_ActiveView.ScreenDisplay);
// Get the graphic's extent.
IEnvelope ext = new EnvelopeClass();
(exporter as ICachedGraphic).QueryBounds(m_ActiveView.ScreenDisplay, ref ext);
// Export the image.
Exporter.ExportToFile(m_ActiveView.ScreenDisplay, filepath, ext, 128, 128, 128, 1.0,
128, false);
[VCPP]
// Exporting a MOLE graphic to a file
LRESULT TestApplication::OnBitmapClicked(WORD wNotifyCode, WORD wID, HWND
hWndCtl, BOOL &bHandled)
{
// create a background color (white)
IRgbColorPtr ipColor(CLSID_RgbColor);
ipColor->put_Red(255);
ipColor->put_Green(255);
ipColor->put_Blue(255);
// create a sample force element graphic
ICachedGraphicPtr ipGraphic = BuildFE(NULL);
// get the map’s screen display
// IActiveViewPtr m_ipActiveView set prior to here
IScreenDisplayPtr ipScreenDisplay;
m_ipActiveView->get_ScreenDisplay(&ipScreenDisplay);
// export the bitmap
ICreateBitmapPtr ipBitmapper(ipGraphic);
IPictureDispPtr ipPicture;
ipBitmapper->DrawToPicture(ipScreenDisplay, 64, 64, 1.5, ipColor, &ipPicture);
// get the image handle
IPicturePtr ipPict(ipPicture);
OLE_HANDLE handle;
ipPict->get_Handle(&handle);
// NOTE: IDC_BITMAP_BUTTON is the resource ID for the button
// put the image on the button
HBITMAP hBMP = (HBITMAP)CopyImage((HBITMAP)handle, IMAGE_BITMAP, 0, 0,
LR_COPYRETURNORG);
CButton *pButton = (CButton*)GetDlgItem(IDC_BITMAP_BUTTON);
pButton->SetBitmap((HBITMAP)hBMP);
return 0;
}
The following code example demonstrates exporting a MOLE cached graphic to memory. In this example, the in-memory graphic is subsequently placed in a picture box.
[VB.NET]
' Create a sample graphic.
Dim createbmp As ICreateBitmap = TryCast(BuildFEG(Nothing, 1, 1, DEFAULT_SIC, DEFAULT_NAME, DEFAULT_PARENT), ICreateBitmap)
' Create a background color.
Dim RGB As IRgbColor = New RgbColorClass()
RGB.Red = 254
RGB.Green = 254
RGB.Blue = 254
' Export the bitmap.
Dim bmpHandle As Integer = createbmp.DrawToDIB(Nothing, 128, 128, 2.0, RGB)
' Create the bitmap from the handle.
Dim bmp As Bitmap = Bitmap.FromHbitmap(New IntPtr(bmpHandle))
' Stamp it on the picture box.
picBitmap.Image = bmp
picBitmap.SizeMode = PictureBoxSizeMode.StretchImage
[C#]
' Create a sample graphic.ICreateBitmap createbmp = BuildFEG(null, 1, 1, DEFAULT_SIC,
DEFAULT_NAME, DEFAULT_PARENT)as ICreateBitmap;
// Create a background color.
IRgbColor rgb = new RgbColorClass();
rgb.Red = 254;
rgb.Green = 254;
rgb.Blue = 254;
// Export the bitmap.
int bmpHandle = createbmp.DrawToDIB(null, 128, 128, 2.0, rgb);
// Create the bitmap from the handle.
Bitmap bmp = Bitmap.FromHbitmap(new IntPtr(bmpHandle));
// Stamp it on the picture box.
picBitmap.Image = bmp;
picBitmap.SizeMode = PictureBoxSizeMode.StretchImage;
[VCPP]
// Exporting a MOLE graphic to memory
LRESULT TestApplication::OnExportClicked(WORD wNotifyCode, WORD wID, HWND
hWndCtl, BOOL &bHandled)
{
// get the visible bounds of the map’s display transformation
// IActiveViewPtr m_ipActiveView set prior to here
IScreenDisplayPtr ipScreenDisplay;
m_ipActiveView->get_ScreenDisplay(&ipScreenDisplay);
IDisplayTransformationPtr ipDT;
ipScreenDisplay->get_DisplayTransformation(&ipDT);
IEnvelopePtr ipVisibleBounds;
ipDT->get_VisibleBounds(&ipVisibleBounds);
// build a sample force element graphic
ICachedGraphicPtr ipGrahic = BuildFE(NULL);
// QI for export interface & export the graphic
IExportGraphicPtr ipExport(ipGraphic);
if (ipExport != NULL)
{
VARIANT_BOOL success;
ipExport->ExportToFile(ipScreenDisplay, CComBSTR(� � �c: / temp /
graphic.gif � � �), ipVisibleBounds, 100, 150, 200, 1.2, VARIANT_FALSE,
&success);
}
return 0;
}
Rotating cached graphics
Individual cached graphics can be rotated using the Angle property on the ICachedGraphic interface. The Angle property uses a value expressed in radians. The following code example demonstrates rotating graphics using a radian value:
[VB.NET]
' Set the angle.
cached.Angle = 45
[C#]
// Set the angle.
cached.Angle = 45;
Cached graphic layers
Cached graphic layers are cached graphics attached to features and, similar to display lists, act as custom containers for collections of cached graphic objects. Both MOLE force elements and tactical graphic layers manage graphics that are associated with features in a feature layer. MOLE layers effectively contain or wrap feature layers, providing military graphics that symbolize the features in those feature layers.
MOLE layers persist all the graphic properties needed to save and restore the appearance of military symbology and implement the following ArcObjects layer interfaces:
- IFeatureLayerSelectionEvents
- IObjectClassEvents
- ILayer
- ILayerEffects
- IPersistStream
- IPersistVariant
- IGlobeRasterizedSupport
The ICachedGraphicFeatureLayer interface manages the handling of the feature characteristics of cached graphic layers and is implemented mutually by the ForceElementLayer and TacticalGraphicLayer classes. These classes are custom layers and are bound to a feature layer and, by wrapping a force element or tactical graphic display list, enable the cached graphic contents of those lists to be viewed and manipulated in an ArcGIS map display.
You can use ICachedGraphicFeatureLayer to access the feature information associated with a layer. ICachedGraphicFeaureLayer links the display list to the feature layer and is used to symbolize the underlying feature layers. ICachedGraphicFeatureLayer enables a feature record in a feature class to be linked to a graphic in a MOLE layer. ICachedGraphicFeatureLayer and IFeatureGraphic are glue interfaces that tie a feature class to a MOLE layer and, within that, a feature to a MOLE graphic.
The following diagram shows the details of the ICachedGraphicFeatureLayer interface:
ICachedGraphicLayer uses a basic refresh to redraw the contents of a layer. A refresh is passed to a display list and causes the display list to draw its contents. When a layer is drawn, MOLE checks whether the layer is dirty, meaning the display cache needs to be rebuilt. If the layer is dirty, the display list clears and rebuilds its graphics, then passes the updated contents of the display list to the display.
The ICachedGraphicLayer2 interface allows you to check the symbology standard in force for the underlying feature class. This interface also allows a custom ICallBack object to be associated with a layer to provide a means to extend or modify the default behavior of a MOLE layer.
MOLE features and layers
MOLE caches its graphics in its own layer cache. For example, when a feature is deleted in ArcMap, the MOLE layer gets an OnDelete event from the feature (object) class, then updates its cache. When manipulating MOLE layers independent of a feature class, it is necessary to make sure the MOLE cache is refreshed periodically; otherwise, its internal cache will not be in sync with the underlying feature class. One way to update the cache for a layer to reflect any feature changes is to call the ICachedGraphicLayer.Refresh method, which invokes a graphic refresh. Use the ICachedGraphicFeatureLayer interface to set up a periodic update of the MOLE layer's cache.
When editing features, the OnChange, OnCreate, and OnDelete ObjectClassEvents fire when you make changes to features, but if the database is changing via other means, the MOLE layers do not receive those notifications. Thus, it may be necessary to force refreshing of the MOLE layer cache. MOLE uses its own graphic cache because of the time-consuming nature of symbol creation and also because the MOLE cache is where leaders and stacks are maintained (since they are not features).
Use the FeatureLayer property of a MOLE layer's ICachedGraphicFeatureLayer interface to manage the feature layer to be drawn or rendered by that MOLE layer. The FeatureLayer property can be used to access the IFeatureClass being symbolized, which can be used to access the feature class properties of the feature layer (in MOLE).
MOLE symbol height
The height of a MOLE graphic is generally set as a ratio of the data frame extent or envelope. The IForceElementLayer interface provides access to control the symbol height of force element graphics in a layer.
The following diagram shows the details of the IForceElementLayer interface:
Use the Size property to define the height of the force element graphics in a layer. The Size property is normally expressed in map units and interpreted by MOLE as such; optionally, it can be interpreted by MOLE as a percentage of the display height. When the display scale changes—for example, if you zoom in or out—graphics appear to become larger or smaller, as their heights remain in map units.
The dynamic symbol size option maintains a graphic's height proportional to the current height of the map view and can be enabled or disabled using the SizeIsRatio property. Be aware of the performance implications when using dynamic symbol sizing. Changing a symbol's size each time the map scale changes requires that a graphic be rebuilt. When a display scale change occurs, such as after a zoom in, zoom out, or any map height change, the vector shapes must be rescaled and retranslated from the single copies kept in the MOLE symbol library. Additionally, when a graphic symbol size change occurs, associated graphic group components require scale adjustments, such as the spacing between graphics in a leader. Dynamic symbol sizing is not the default graphic size due to these performance impacts. You should programmatically refresh or rebuild MOLE graphics after resizing the graphics.
The following code example shows how to set the graphic Size property for a given force element graphic:
[VB.NET]
' Set the size based on the active view extent
Dim Size As Double = m_ActiveView.Extent.Height * 0.075
cached.Size = Size
[C#]
// Set the size based on the active view extent
double size = m_ActiveView.Extent.Height * 0.075;
cached.Size = size;
Attribute labeling
MOLE allows you to draw text annotation in two ways: using polygon text or using TrueType fonts. In addition to the 15-character code, MOLE supports MIL-STD-2525B Change 2 and APP-6A defined text modifiers (fields) that provide additional information about a symbol and displays them surrounding a symbol.
Attribute values can be created or modified using MOLE's editor tools and saved with the units to a new file for later use or in other 2525B-compliant applications. MOLE references attribute fields by name so they can exist and be accessed in order in the table.
The following are the default MIL-STD-2525B Change 2 and APP-6A attribute text modifiers:
- Latitude {number, decimal degrees}
- Longitude {number, decimal degrees}
- Symbol_ID {the 15-character message string}
- Quantity {character 255}
- Reinforced_Reduced {character 255}
- Staff_Comments {character 255}
- Additional_Information {character 255}
- Evaluation_Rating {character 255}
- Combat_Effectiveness {character 255}
- Signature_Equipment {character 255}
- Higher_Formation {character 255}
- Iff_Sif {character 255}
- Unique_Designation {character 255}
- Equipment_Type {character 255}
- Drawing Graphic Text
- Graphic Text Factory
Use the AttributeLabel class and IAttributeLabel interface to provide information necessary to graphically depict attribute information associated with a cached graphic. The following diagram shows the details of the AttributeLabel class:
You can use default graphic text with symbols or manipulate the display and behavior of graphic text at very low levels. For example, a specific text attribute can be made large to notify the user of an attribute change or modify the default way graphics display. Modifying the IAttributeLabel properties results in a relative change in all ForceElement symbols generated from then on.
The FEGraphicFactory class implements IEnumAttributeLabel. IEnumAttributeLabel retrieves the text modifier string values from an attribute field and places them around a force element graphic in the locations defined in MIL-STD-2525B Change 2 and APP-6A. Use IEnumAttributeLabel to control height, justification, location, and visibility for a given force element graphic.
The following diagram shows the details of the IEnumAttributeLabel interface:
Drawing and manipulating attribute labels is performance intensive. Drop attribute labels altogether, if possible, or minimize the number of labeled force elements or visible labels.
You can modify the placement and value of an attribute label after a graphic has been constructed. The following code example demonstrates modifying a graphic's attribute text after the graphic has been built and refreshed:
[VB.NET]
' Modifying a graphic's attribute text after the graphic has been built and refreshed.
' Create a font.
Dim font As IFontDisp = ESRI.ArcGIS.ADF.Converter.ToStdFont(New System.Drawing.Font("Courier New", 20, System.Drawing.FontStyle.Bold))
' Create the text symbol.
Dim textsymbol As ITextSymbol = New TextSymbolClass()
textsymbol.Font = font
' Create an FE graphic style.
Dim style As IFEGraphicStyle = New FEGraphicStyleClass()
style.Name = "My New Style"
style.UseFonts = True
style.TextSymbol = textsymbol
' Create a new graphic.
Dim cached As ICachedGraphic = BuildFEG(style, -97.75, 30.85, DEFAULT_SIC, DEFAULT_NAME, DEFAULT_PARENT)
' Set the size based on the map extent.
Dim Size As Double = m_ActiveView.Extent.Height * 0.075
cached.Size = Size
' Get the display.
Dim screendis As IDisplay = m_ActiveView.ScreenDisplay
' Refresh the graphic.
cached.Refresh(screendis)
' Tweak the labels.
Dim labeled As ILabeledGraphic = TryCast(cached, ILabeledGraphic)
Dim [Set] As ISet = labeled.AttributeText
' Cycle through the set.
[Set].Reset()
Dim text As IAttributeText = TryCast([Set].Next(), IAttributeText)
Do While Not text Is Nothing
If text.Name = "Name" Then
' Increase the size of just one label.
text.Height = text.Height * 2
Exit Do
End If
' Go on to the next one.
text = TryCast([Set].Next(), IAttributeText)
Loop
' Add it to the map.
Sizify(cached)
AddToMap(cached)
[C#]
//Modifying a graphic's attribute text after the graphic has been built and refreshed.
private void btnModifyGraphicLabels_Click(object sender, EventArgs e)
{
// Create a font.
IFontDisp font = new StdFontClass()as IFontDisp;
font.Name = "Courier New";
font.Bold = true;
// Create the text symbol.
ITextSymbol textsymbol = new TextSymbolClass();
textsymbol.Font = font;
// Create an FE graphic style.
IFEGraphicStyle style = new FEGraphicStyleClass();
style.Name = "My New Style";
style.UseFonts = true;
style.TextSymbol = textsymbol;
// Create a new graphic.
ICachedGraphic cached = BuildFEG(style, - 97.75, 30.85, DEFAULT_SIC,
DEFAULT_NAME, DEFAULT_PARENT);
// Set the size based on the map extent.
double size = m_ActiveView.Extent.Height * 0.075;
cached.Size = size;
// Get the display.
IDisplay screendis = m_ActiveView.ScreenDisplay;
// Refresh the graphic.
cached.Refresh(screendis);
// Tweak the labels.
ILabeledGraphic labeled = cached as ILabeledGraphic;
ISet set = labeled.AttributeText;
// Cycle through the set.
set.Reset();
IAttributeText text = set.Next()as IAttributeText;
while (text != null)
{
if (text.Name == "Name")
{
// Increase the size of just one label.
text.Height = text.Height * 2;
break;
}
// Go on to the next one.
text = set.Next()as IAttributeText;
}
// Add it to the map.
Sizify(cached);
AddToMap(cached);
}
[VCPP]
// Modifying a graphics attribute text after the graphic has been built and refreshed
LRESULT TestApplication::OnLabelsClicked(WORD wNotifyCode, WORD wID, HWND
hWndCtl, BOOL &bHandled)
{
// create the font
IFontPtr ipStdFont(CLSID_StdFont);
ipStdFont->put_Name(CComBSTR("Courier New"));
ipStdFont->put_Bold(TRUE);
IFontDispPtr ipFont(ipStdFont);
// create the text symbol
ITextSymbolPtr ipTextSymbol(CLSID_TextSymbol);
ipTextSymbol->put_Font(ipFont);
// create the graphic style
IFEGraphicStylePtr ipNewStyle(CLSID_FEGraphicStyle);
ipNewStyle->put_Name(CComBSTR(� � �My New Style � � �));
ipNewStyle->put_UseFonts(VARIANT_TRUE);
ipNewStyle->putref_TextSymbol(ipTextSymbol);
// use a helper function to create the force element graphic
ICachedGraphicPtr ipGraphic = BuildFE(ipNewStyle);
// get the map’s active view & display
// IActiveViewPtr m_ipActiveView set prior to here
IScreenDisplayPtr ipScreenDisplay;
m_ipActiveView->get_ScreenDisplay(&ipScreenDisplay);
// get the active view’s extent height
IEnvelopePtr ipExtent;
ipAV->get_Extent(&ipExtent);
double height;
ipExtent->get_Height(&height);
// set the graphic size
ipGraphic->put_Size(0.075 *height);
ipGraphic->Refresh(ipScreenDisplay);
// get the graphic’s set of text attributes
ILabeledGraphicPtr ipLabeledGraphic(ipGraphic);
ISetPtr ipTextSet;
ipLabeledGraphic->get_AttributeText(&ipTextSet);
// set the text attributes’ sizes
ipTextSet->Reset();
IUnknownPtr ipUnk;
ipTextSet->Next(&ipUnk);
while (ipUnk != NULL)
{
IAttributeTextPtr ipAttributeText(ipUnk);
CComBSTR name;
ipAttributeText->get_Name(&name);
if (name == CComBSTR(� � �Name � � �))
{
double attHeight;
ipAttributeText->get_Height(&attHeight);
ipAttributeText->put_Height(2 *attHeight);
}
ipTextSet->Next(&ipUnk);
}
// draw the graphic
long hDC;
ipScreenDisplay->get_hDC(&hDC);
ipScreenDisplay->StartDrawing(hDC, 0);
ipGraphic->Draw(ipScreenDisplay, NULL);
ipScreenDisplay->FinishDrawing();
return 0;
}
Extending the MOLE API
By default, the objects in the MOLE API comply with rendering military symbology according to MIL-STD-2525B Change 2 and APP-6A. These objects, however, can be extended further to support customized symbology outside the MIL-STD-2525B Change 2 and APP-6A specifications. You have access to many objects in the library that expose control over symbology components and rendering methods down to a low level of functional granularity. In addition to MIL-STD-2525B Change 2 and APP-6A, you can extend the MOLE API to customize the following:
- Attribute field names
- Force element graphic styles
- Force element renderers
- Tactical graphic renderers
- Selection/Highlight symbology
- Hull buffering symbology for groups
- Callout line symbology for groups
- Force element primitives used to construct symbols
Refer to the MOLE developer samples for additional examples of how to extend the MOLE API.
For further information see:
Military Symbols for Land Based Systems - APP-6AUsing military symbology and symbol-based graphics
Sample: Displaying symbol-based MOLE graphics on a MapControl
Sample: Decluttering MOLE graphics using leadering and stacking
Sample: Arranging MOLE graphics using manual decluttering
Sample: Using MOLE symbol-based graphics with interactive maps
Sample: Displaying MOLE symbology with the GlobeControl
Converting coordinates
The DefenseSolutions library provides objects that allow you to add coordinate conversion functions to your applications. These objects are Coordinate and CoordinateTool.
Coordinate
The Coordinate class provides the following capabilities:
- Parsing and validation of text representations of coordinates
- Conversion between a coordinate's text representation and its geodetic (latitude/longitude) representation and vice versa
- Conversion between different coordinate formats
- Supports automatic and user-specified geotransformation
The Coordinate class implements the ICoordinate interface, which is shown in the following diagram:
There are a number of ways to put or get geographic locations into or from a Coordinate object. These methods are symmetrical, so you can use them for both input or output. On input, the coordinate value is converted internally so that you can retrieve the results from either the same property (or method) or one of the others. If the input is an array type, then you must use an array method to fetch the results.
The output values are established immediately after input. You cannot change an option, geotransform, precision, spatial reference, parsing expression, or formatting expression, then fetch new results reflecting that change. If you change something, you must input the values again.
The Point property takes or returns a pointer to a Point object (geodetic coordinate). The geometry of the point is used as the location of the Coordinate object. The spatial reference of the point is ignored. Use the InputSpatialReference and OutputSpatialReference properties instead. The String property takes a string in a format suitable for the coordinate. On output, it returns a normalized version of the input. The IsValid property can be used to check if the input string was parsed. PutCoords and GetCoords are utility methods that keep you from having to create a Point object to put or get a coordinate. PointArray and StringArray are array versions of the Point and String properties. They work in exactly the same way. The array elements are matched—element 1 in the PointArray matches element 1 in the StringArray, and so on. The IsValid property does not work for these properties. Instead, the array contains a null pointer or an empty string to indicate that parsing or conversion failed for that element.
Modes of operation
The Coordinate object can be used in the following modes:
- Input a string representation of a coordinate and extract it as decimal degrees
- Input a Point and extract it as a string
- Input a coordinate in one spatial reference and extract it in another
- Input a coordinate in one format (for example, Degrees Minutes Seconds [DMS]) and output it in another format (for example, Military Grid Reference System [MGRS]). This involves using two Coordinate objects. Use the AddOutputCoordinate or OutputCoordinateSet property to add target coordinates to the source Coordinate object. When you input a value to the source Coordinate, you can fetch the output value from the target coordinate(s). This works for any input and output, even arrays.
One-way conversion is shown in the following graphic:
Do not specify bidirectional conversion, or any cycles, such as those shown in the following two-way conversion:
Cycles are detected and ignored.
Although it's possible to have a coordinate as the target of multiple source Coordinates, the results are undefined.
The following code example shows how to get an array of points from a shapefile and convert it to arrays of strings in various formats.
[VB.NET]
' Load shapefile points into array.
Dim workspaceFactory As IWorkspaceFactory
workspaceFactory = New ShapefileWorkspaceFactory
Dim workspace As IWorkspace
workspace = workspaceFactory.OpenFromFile("path to shapefile directory", 0)
'Cast to IFeatureWorkspace.
Dim featureWorkspace As IFeatureWorkspace
featureWorkspace = workspace
Dim featureClass As IFeatureClass
featureClass = featureWorkspace.OpenFeatureClass("name of shape file")
Dim Count As Integer
Count = featureClass.FeatureCount(Nothing)
Dim pointArray As IArray
pointArray = New Array
If (Count > 20) Then
Count = 20
End If
Dim f As IFeature
Dim g As IGeometry
Dim p As IPoint
Dim x As Double
Dim y As Double
For i As Integer = 0 To Count
f = featureClass.GetFeature(i)
g = f.Shape
p = g
p.QueryCoords(x, y)
pointArray.Add(p)
Next i
' Set up the SRs.
Dim SREnv As SpatialReferenceEnvironment
SREnv = New SpatialReferenceEnvironment
Dim WGS1972_SR As ISpatialReference
WGS1972_SR = SREnv.CreateGeographicCoordinateSystem(esriSRGeoCSType.esriSRGeoCS_WGS1972)
Dim mgrsCoord As MGRSCoordinate
mgrsCoord = New MGRSCoordinate
mgrsCoord.Precision = esriCoordinatePrecision.esriCPOneCentimeter
' Seven coordinate digits per axis.
Dim dmsCoord As DMSCoordinate
dmsCoord = New DMSCoordinate
dmsCoord.Precision = esriCoordinatePrecision.esriCPOneTenThousandthSecond
dmsCoord.OutputSpatialReference = WGS1972_SR
Dim ddCoord As DDCoordinate
ddCoord = New DDCoordinate
ddCoord.Precision = esriCoordinatePrecision.esriCPOneTenthThousandthDegree
ddCoord.OutputSpatialReference = WGS1972_SR
' Forwarded coordinates get their InputSR from the calling coordinate.
' Forward the output.
mgrsCoord.AddOutputCoordinate(dmsCoord)
mgrsCoord.AddOutputCoordinate(ddCoord)
' Perform the input.
mgrsCoord.PointArray = pointArray
' Get the output.
Dim mgrsStringArray As IStringArray
Dim dmsStringArray As IStringArray
Dim ddStringArray As IStringArray
mgrsStringArray = New StrArray
dmsStringArray = New StrArray
ddStringArray = New StrArray
mgrsStringArray = mgrsCoord.StringArray
dmsStringArray = dmsCoord.StringArray
ddStringArray = ddCoord.StringArray
For i As Integer = 0 To Count
p = pointArray.Element(i)
p.QueryCoords(x, y)
Console.WriteLine("Point[x, y] = " & x & " " & y)
Console.WriteLine("dd = " & ddStringArray.Element(i))
Console.WriteLine("mgrs =" & mgrsStringArray.Element(i))
Console.WriteLine("dms = " & dmsStringArray.Element(i))
Console.WriteLine()
Next
[C#]
// Load shapefile points into array.
IWorkspaceFactory workspaceFactory = new
ESRI.ArcGIS.DataSourcesFile.ShapefileWorkspaceFactoryClass();
IWorkspace workspace = workspaceFactory.OpenFromFile(@"path_of_shapefile", 0);
//Cast to IFeatureWorkspace.
IFeatureWorkspace featureWorkspace = (IFeatureWorkspace)workspace;
IFeatureClass featureClass = featureWorkspace.OpenFeatureClass("name_of_shapfile");
int count = featureClass.FeatureCount(null);
IArray pointArray = new ArrayClass();
if (count > 20)
count = 20;
for (int i = 0; i < count; i++)
{
IFeature f = featureClass.GetFeature(i);
IGeometry g = f.Shape as IGeometry;
IPoint p = g as IPoint;
double x, y;
p.QueryCoords(out x, out y);
pointArray.Add(p);
}
// Set up the SRs.
SpatialReferenceEnvironment SREnv = new SpatialReferenceEnvironment();
ISpatialReference WGS1972_SR = SREnv.CreateGeographicCoordinateSystem((int)
esriSRGeoCSType.esriSRGeoCS_WGS1972);
dmsCoord.OutputSpatialReference = WGS1972_SR;
// Forwarded coordinates get their InputSR from the calling coordinate
ddCoord.OutputSpatialReference = WGS1972_SR;
// but need to have their OutputSR specified (it would be WGS84 otherwise).
MGRSCoordinate mgrsCoord = new MGRSCoordinate();
mgrsCoord.Precision = esriCoordinatePrecision.esriCPOneCentimeter;
// Seven coordinate digits per axis.
DMSCoordinate dmsCoord = new DMSCoordinate();
dmsCoord.Precision = esriCoordinatePrecision.esriCPOneTenThousandthSecond;
DDCoordinate ddCoord = new DDCoordinate();
ddCoord.Precision = esriCoordinatePrecision.esriCPOneTenthThousandthDegree;
// Forward the output.
mgrsCoord.AddOutputCoordinate(dmsCoord);
mgrsCoord.AddOutputCoordinate(ddCoord);
// Perform the input.
mgrsCoord.PointArray = pointArray;
// Get the output.
IStringArray mgrsStringArray = new StrArrayClass();
IStringArray dmsStringArray = new StrArrayClass();
IStringArray ddStringArray = new StrArrayClass();
mgrsStringArray = mgrsCoord.StringArray;
dmsStringArray = dmsCoord.StringArray;
ddStringArray = ddCoord.StringArray;
Console.WriteLine("Input (point DD x, y WGS84) = dd(WGS1972), mgrs, dms(WGS1972)");
for (int i = 0; i < count; i++)
{
double x, y;
IPoint p = pointArray.get_Element(i);
p.QueryCoords(out x, out y);
Console.WriteLine("Point[x, y] = " + x + " " + y);
Console.WriteLine("dd = " + ddStringArray.Element(i));
Console.WriteLine("mgrs =" + mgrsStringArray.Element(i));
Console.WriteLine("dms = " + dmsStringArray.Element(i));
Console.WriteLine();
}
CoordinateTool
The CoordinateTool object is the engine behind the Military Analyst Coordinate tool and the Convert Coordinates in File command in ArcMap. It allows you to convert coordinates to and from decimal degree, DMS, universal transverse Mercator (UTM), and MGRS formats, as well as from one datum to another.
CoordinateTool implements a single interface: ICoordinateTool. ICoordinateTool provides the following three methods:
ConvertLocation is the key method, as it is the one used for coordinate conversion. Two of the parameters of note for ConvertLocation are vFromDatum and vToDatum, the input and output datums, respectively, for the coordinate conversion. The values passed into these parameters can either be a string representing the name of the datum or a long representing the index of the datum in the coordinate tool's datum list. GetDatumList returns a string array listing the datum strings that are supported by the ConvertLocation method. It can also be used to return the datum name string at a given index. The list returned by GetDatumList is the same as the Datum drop-down list in the Military Analyst Coordinate Tool dialog box in ArcMap. GetDatumEllipsoid returns the ellipsoid, also known as the spheroid, used by a specified datum.
The following diagram details the ICoordinateTool interface:
For further information see:
Defense solutions coordinate API input-output formatsSample: Locate coordinates
Creating geodetically correct geometric objects
The DefenseSolutions library's Geodesy API provides objects for measuring geodesic, rhumb line, and great circle distances; creating geodetically correct geometric objects; and creating dynamic graphic element representations of these geometries for display. The Geodesy API is the engine behind the Military Analyst Geodesy Calculator, Geodesy Graphic tool, and Range Rings tool in ArcMap. The Geodesy API is comprised of the following classes: MeasurementTool, GeoGeometry, and GeoElements.
MeasurementTool
The MeasurementTool class implements a single interface, IMeasurementTool, that provides methods for calculating geodesic, great circle, and rhumb line distances. Geodesics represent the shortest distance between two points on a spheroid; great circles represent the shortest distance between two points on a sphere; and rhumb lines, also known as loxodromes, are lines of constant azimuth. Incidentally, rhumb lines are always straight lines in a Mercator projection.
The following diagram details the IMeasurementTool interface:
IMeasurementTool can be used to calculate the distance and azimuth values of a line based on input start and end points by utilizing the ConstructByPoints method, or to calculate the end point of a line based on input start point, distance, and azimuth values by utilizing the ConstructByPointDistAngle method. In either case, the spatial reference of the input IPoint must be specified before they are passed to the particular construct method.
ISpatialReference must be defined for the IMeasurementTool.SpecialSpatialReference property because the results of the construct methods are dependent on the input spheroid, which is inherent to ISpatialReference. If SpecialSpatialReference is not defined explicitly, it will default to esriSRGeoCS_WGS1984. The SpecialGeolineType property defines the type of line (geodesic, great circle, or rhumb line) that will be constructed, using the cjmtkSGType enumeration.
The SpecialGeolineType property must be set after the particular construct method is called. You can also return IPolyline representing the geoline created by the construct method using the Polyline property on IMeasurementTool. IPolyline is densified by the number of vertices specified on the property. This polyline geometry can then be stored in a feature, displayed as a graphic, or used in any other operation where such a geometry is relevant.
Additional methods on IMeasurementTool include GetCoordinate and PathDistance. GetCoordinate returns IPoint at a specified location, as a percentage of the total distance of a geoline, along the geoline.
GeoGeometry
The GeoGeometry classes include GeoPolyline, GeoPolygon, and GeoEllipse. These objects allow for the construction of geodetically correct, spatial reference-aware versions of standard polyline and polygon geometries. They also work directly with the geoelement objects for display as dynamic graphic elements.
The GeoPolyline class implements the IGeoPolyline interface, which provides members for the creation of special polyline geometries that represent geodesic, great circle, and rhumb lines. This is different from IMeasurementTool, which is used to measure geodesic, great circle, and rhumb line distances. IGeoPolyline is focused solely on the generation of the special polyline geometries for their representation in a graphic element or a feature.
The first step in creating a geopolyline is to define a two-point polyline, which provides the start and end points of the geopolyline. The input polyline must have a spatial reference defined for it. The correct ISpatialReference must also be specified for IGeoPolyline.BaseSpatialReference, as this is critical to the construction of the geopolyline. If the BaseSpatialReference is not set, it defaults to esriSRGeoCS_WGS1984.
The type of GeoPolyline that's created is determined by the SpecialGeolineType property, which uses the cjmtkSGType enumeration. Additional properties controlling the number of intermediate vertices generated between the start and end points of the GeoPolyline can be specified as well. These are the MaxPercent and MaxStepSize properties. With the MaxPercent property, a percentage of the total distance of the line can be specified, and a vertex point is placed along the line at distance intervals corresponding to this value. In other words, if a value of 0.1, or 10 percent, is specified, nine vertices will be created, dividing the line into ten segments. The MaxStepSize property defines the maximum distance in meters between vertices along the line. If neither MaxPercent nor MaxStepSize is specified, then MaxPercent is used and defaults to a value of 0.05, or 5 percent.
The following diagram details the IGeoPolyline interface:
The EnhancedPolyline property on IGeoPolyline returns IPolyline representing the special geoline version of the original input two-point polyline. This polyline geometry can be stored in a feature or used in any other operation where such a geometry is relevant.
The GeoPolygon class implements the IGeoPolygon interface, which provides members for the creation of special polygon geometries whose constituent segments are composed of geodesic lines, great circle lines, and rhumb lines. IGeoPolygon provides many of the same members as IGeoPolyline, and its usage is similar as well. As with IGeoPolyline, the purpose of IGeoPolygon is to generate special polygon geometries for their representation in a graphic element or a feature.
The first step in creating a GeoPolygon is to define an input Polygon, which provides the base geometry of the GeoPolygon. The input Polygon must have a spatial reference defined for it. The correct ISpatialReference must also be specified for IGeoPolygon.BaseSpatialReference, as this is critical to the construction of the GeoPolygon. If BaseSpatialReference is not set, it defaults to esriSRGeoCS_WGS1984.
The SpecialGeolineType property determines the type of geoline (geodesic, great circle, or rhumb line) by which the GeoPolygon's segments will be represented. The MaxPercent and MaxStepSize properties function the same as the properties on IGeoPolyline; thus, the previous description of them applies here as well. The EnhancedPolygon property also has the same usage as the IGeoPolyline.EnhancedPolyline property; see the description of it in the previous section. The code examples for IGeoPolyline also apply to IGeoPolygon. Simply replace polyline with polygon in all cases.
The following diagram details the IGeoPolygon interface:
The GeoEllipse class implements the IGeoEllipse interface, which provides members for the creation of special elliptical polygons. As with the other GeoGeometry objects, the first step in creating a GeoEllipse is to define an input geometry that will form the basis of the GeoEllipse. In this case, the input geometry is IPoint, which represents the origin, or center point, of the GeoEllipse. IPoint must have a defined spatial reference. The point is passed to GeoEllipse via the IGeoEllipse.Origin property. GeoEllipse must also have ISpatialReference defined and set via the BaseSpatialReference property. The default is esriSRGeoCS_WGS1984.
The GeoEllipse dimensions are controlled by the AxisX and AxisY properties, which define the lengths, in meters, of the GeoEllipse X axis and Y axis respectively. Optionally, GeoEllipse can be rotated by passing in a value for the Rotation property. The Rotation value is a double that specifies a rotation angle in degrees from north for the Y axis of GeoEllipse. The final property to be defined for GeoEllipse is GeoEllipsePointCount, which, like the nNumberPoints parameter on IMeasurementTool.Polyline, defines the number of vertices by which to densify the GeoEllipse polygon.
The following diagram details the IGeoEllipse interface:
The object created by IGeoEllipse is GeoEllipse, which can be passed as the geometry to GeoEllipseElement for depiction as a graphic element. The IGeoEllipse.Polygon property refers to the IPolygon representation of GeoEllipse, which can be stored in a feature or used in another operation where such a geometry is relevant. A third entity that can be generated from the input properties is IGeoEllipse.KeyPoints, which is a four-point IPolygon with points at the ends of the GeoEllipse axes.
GeoElement
The GeoElement classes include GeoPolylineElement, GeoPolygonElement, and GeoEllipseElement. These are special dynamic graphic elements whose source geometry is provided by GeoGeometry objects (GeoPolylines for GeoPolylineElements, GeoPolygons for GeoPolygonElements, and GeoEllipses for GeoEllipseElements). These objects are dynamic in the sense that their shape updates automatically when they are moved from one location to another, or if the map spatial reference is changed. See the following illustrations for examples of the dynamic nature of GeoEllipseElement:
As GeoEllipseElement is moved, its GeoEllipse geometry is recalculated based on the new location of the origin point. Thus, it maintains its spatial accuracy regardless of its geographic location or the spatial reference of the map as shown in the following illustration:
For further information see:
Sample: Geodesy MapControlControlling the Military Analyst GeoSym Renderer
The GeoSym Renderer API provides objects for symbolizing feature data in ArcGIS according to the GeoSym specification. GeoSym is the standard that defines how Vector Product Format (VPF) data is to be symbolized. The GeoSymRenderer class is the only class available in the API, and it implements a single interface—IGSGeoSymRenderer. Additionally, there are two other interfaces available in the API: IGSGeoSymRenderer2 and IGSRenderingProfile.
Creating and displaying Military Analyst layers
The DefenseSolutions library provides the MADtedLayer and MARasterLayer classes for creating and displaying Military Analyst Layers.
MADtedLayer
The MADtedLayer object is a custom layer used to draw Military Analyst digital terrain elevation data (DTED) catalogs in ArcMap or a MapControl. It allows you to draw the catalog with a custom Uniform Renderer, set the display resampling method, and display a color-coded wireframe. The MADtedLayer class implements the following interfaces:
- IMADatasets—Three properties are on IMADatasets: Dataset, DatasetCount, and UseDataset. Dataset returns the name of the dataset at the specified index. DatasetCount returns the number of datasets. UseDataset sets the product for display.
- IMADtedLayer—This interface is the core of MADtedLayer and sets the catalog table for the layer. It also controls the resampling and display of the individual dataset tiles. IMADtedLayer has the following properties and methods:
- DisplayElevation—If set to true, will draw all the dataset tiles in the catalog
- InternalObject—Returns the layer as a raster catalog
- ResamplingType—Sets the raster resampling method used to draw individual tiles
- UseUniformRenderer—Sets a custom stretch renderer across all tiles so the catalog appears as if it were one single dataset
- Setup—The core method. It takes ITable of a DTED catalog and builds MADtedLayer. The returned Boolean is true if layer initialization is successful.
- IWireframeDisplayProps—DisplayWireframe is the only property on this interface, and it has type esriMAWireframeDisplayEnum. A value of esriWireframeDisplayNone turns off wireframe display for this layer, esriWireframeDisplaySelected draws the wireframe for selected products, and esriWireframeDisplayAll draws wireframes for all products regardless of selection.
The following code example shows how to load a DTED layer onto a map:
[VB.NET]
' Get a raster workspace.
Dim rasterWorkspace As IRasterWorkspaceEx = TryCast(m_workSpace, IRasterWorkspaceEx)
Dim catalogName As String = "HawaiiDted"
' Open the raster catalog.
Dim rasterCatalog As IRasterCatalog = rasterWorkspaceEx.OpenRasterCatalog(catalogName)
' Create the MADtedLayer.
Dim dtedLayer As IMADtedLayer = New MADtedLayerClass()
If dtedLayer.Setup(TryCast(rasterCatalog, ITable)) <> True Then
Return
End If
' Set the layer properties.
dtedLayer.DisplayElevation = True
dtedLayer.UseUniformRenderer = True
' Load the DTED layer onto the map.
Dim layer As ILayer = TryCast(dtedLayer, ILayer)
m_map.AddLayer(layer)
[C#]
// Get a raster workspace.
IRasterWorkspaceEx rasterWorkspace = m_workSpace as IRasterWorkspaceEx;
string catalogName = "HawaiiDted";
// Open the raster catalog.
IRasterCatalog rasterCatalog = rasterWorkspaceEx.OpenRasterCatalog(catalogName);
// Create the MADtedLayer.
IMADtedLayer dtedLayer = new MADtedLayerClass();
if (dtedLayer.Setup(rasterCatalog as ITable) != true)
return ;
// Set the layer properties.
dtedLayer.DisplayElevation = true;
dtedLayer.UseUniformRenderer = true;
// Load the DTED layer onto the map.
ILayer layer = dtedLayer as ILayer;
m_map.AddLayer(layer);
MARasterLayer
The MARasterLayer object is a custom layer used to draw Military Analyst Raster Product Format (RPF) catalogs in ArcMap or a MapControl. The MARasterLayer class has the following interfaces:
- IMADatasets—This interface and its properties are the same as those for MADtedLayer, described in the previous section.
- IMARasterLayer—This interface is the core of MARasterLayer and sets the catalog table for the layer. It also controls the resampling and display of the individual dataset tiles. IMARasterLayer has the following properties and methods:
- DisplayRasters—If set to true, draws the tiles of the selected products.
- InternalObject—Returns the raster catalog of the layer.
- ResamplingType—Sets the raster resampling for the dataset tiles.
- UseBestMap—Draws the best RPF product based on the display scale. As the scale changes (Zoom In, Zoom Out), the product displayed changes.
- Setup—Implements the layer.
- IWireframeDisplayProps—This interface and its property are the same as those for MADtedLayer, described in the previous section.
The following code example shows how to load a raster layer onto a map:
[VB.NET]
' Get a raster workspace.
Dim rasterWorkspace As IRasterWorkspaceEx
rasterWorkspace = TryCast(m_workSpace, IRasterWorkspaceEx)
Dim catalogName As String = "HawaiiRaster"
' Open the raster catalog.
Dim rasterCatalog As IRasterCatalog = rasterWorkspaceEx.OpenRasterCatalog(catalogName)
' Create the raster layer.
Dim rasterLayer As IMARasterLayer = New MARasterLayerClass()
If maRasterLayer.Setup(TryCast(rasterCatalog, ITable)) <> True Then
Return
End If
' Set the layer properties.
rasterLayer.DisplayRasters = True
rasterLayer.UseBestMap = True
' Load the raster layer onto the map.
Dim layer As ILayer = TryCast(rasterLayer, ILayer)
m_map.AddLayer(layer)
[C#]
// Get a raster workspace.
IRasterWorkspaceEx rasterWorkspace;
rasterWorkspace = m_workSpace as IRasterWorkspaceEx;
string catalogName = "HawaiiRaster";
// Open the raster catalog.
IRasterCatalog rasterCatalog = rasterWorkspaceEx.OpenRasterCatalog(catalogName);
// Create the raster layer.
IMARasterLayer rasterLayer = new MARasterLayerClass();
if (maRasterLayer.Setup(rasterCatalog as ITable) != true)
return ;
// Set the layer properties.
rasterLayer.DisplayRasters = true;
rasterLayer.UseBestMap = true;
// Load the raster layer onto the map.
ILayer layer = rasterLayer as ILayer;
m_map.AddLayer(layer);
See Also:
Military Symbols for Land Based Systems - APP-6AUsing military symbology and symbol-based graphics
Defense solutions coordinate API input-output formats
Sample: Decluttering MOLE graphics using leadering and stacking
Sample: Arranging MOLE graphics using manual decluttering
Sample: Using MOLE symbol-based graphics with interactive maps
Sample: Displaying MOLE symbology with the GlobeControl
Sample: Displaying symbol-based MOLE graphics on a MapControl
Sample: Locate coordinates
Sample: Geodesy MapControl