Draw force elements


PurposeThis standalone sample ArcGIS Engine application demonstrates a way to create, position, and edit force element graphics on a map. The application provides a map and toolbar control on the right, and a control panel made of Qt widgets on the left. We intend to demonstrate how a complex interactive session can be built up from primitive MOLE objects and methods in conjunction with an application framework like Qt. Several MOLE Classic interfaces and coclasses are used in this sample. ICachedGraphic is used to interactively draw, pick, clip, highlight, scale, and rotate force elements. IFEGraphic and IForceElement are used to interactively edit labels, symbol ID codes, and positions on force element graphics.   IForceElement2525BRenderer is used to create the cached graphics. The MOLE coclasses instantiated are ForceElement, ForceElement2525BRenderer, and MoleCoreHelper. Many other objects are obtained from properties and factories. Additional MOLE interfaces used are IAttributeLabel, IEnumAttributeLabel, IExportGraphic, IFEGraphicFactory, IFEGraphicStyle, and IMoleCoreHelper.   Several other ArcObjects classes and interfaces are used as well, including IMapControlEvents2.

How to use

If the sample has associated data, you will find that the sample's zip file includes a "data" folder alongside the language folders. However, you will need to update the sample to point to the location of the data once you have extracted all the files.

  1. Compile the DrawForceElements project. At the command line, type DrawForceElements to launch the sample. The steps being taken to produce the display are printed on the console. Once the display appears, it can be navigated with the toolbar.
  2. The user interface has three modes, controlled with the radio buttons at top left. The top (initial) mode allows use of the ArcEngine toolbar, and the editor panel is disabled. In the other two modes, the toolbar is disabled to prevent the tools from interacting with the editor panel, which is enabled. The middle mode allows you to place force elements on the map by clicking on it. The bottom mode allows you to move force elements on the map by dragging them.
  3. One force element can be selected at any time; this one is highlighted in cyan. When placing new ones, the mostly recently added one is selected.
  4. You can change the general affiliation of a force element using the lower group of four radio buttons. When placing new ones, the affiliation applies to the next one placed. When editing existing ones, the affiliation changes the symbol on the map interactively. Other affiliation codes exist, but there was only space to have one radio button for each major frame type.
  5. The checkbox causes the symbol ID code to be randomized after each new force element is placed on the map. When cleared, the same symbol will be placed on the map by each mouse click.
  6. The "Symbol ID Code" box allows the symbol ID code to be edited by hand, possibly in consultation with the 2525B standard. When placing new force elements, these edits apply to the next one placed. When editing existing force elements, these edits apply interactively at any time the box contains a valid symbol ID code.
  7. The label editor has a combo box to indicate which label is being displayed and a text box to edit the label. The text below it indicates the placement of the label relative to the symbol frame, following the definitions in Table IV of the 2525B Change 1 standard. When placing force elements, these edits apply to the next force element that is placed. When editing force elements, these edits apply interactively to the selected graphic, if any.
  8. The two sliders can be used to change the size and orientation of all the force elements on the map at one time. The size is computed relative to the current map extent, but is stored in map units. This means that when you zoom the map, the force elements zoom just like the map does, but when you operate the slider again, they "jump" back to a view-relative size.
  9. The "Randomize Symbol ID" button replaces the "Symbol ID Code" with a random one. When placing force elements, this applies to the next element that will be placed. When editing, this applies interactively to the selected graphic, if any.
  10. The "Delete Selected" button deletes the selected graphic, leaving none selected. The button is disabled when no force element is selected.
  11. The "Clear All" button deletes all force elements from the display.
  12. The "Export to PNG" button creates one PNG file in the current directory for each force element on the map. The images contain the symbol and surrounding text.
  13. The class is organized into four groups of functions: private utility functions, slots (Qt event handlers), IMapControlEvents2 listener methods, and the constructor and destructor. The utility functions are used to factor out code that would otherwise be repeated several times in the event handlers. The slots drive all the editing functions except those involving mouse inputs on the map, which are driven by the listener methods.
  14. The constructor populates the label editor’s combo box using the IEnumAttributeLabel interface on the FE graphic factory associated with the 2525B renderer. The contents of the descriptive label below the editor are stored in the same order that the label names are added to the combo box, so they can use a common index. There is one descriptive name for each IAttributeLabel in the enumeration, but in some cases there are several field names in a semicolon-separated list which is parsed out. Each field name becomes a separate entry in the combo box, because the field names are the keys to the property set that is used to store the label text in IForceElement, as can be seen in the copyTextToProp function.
  15. Any time we change any property on the selected graphic, we call ICachedGraphic::Refresh with the map display as a parameter. This causes the object to "pull" any changes from its associated property objects and rebuild its internal data structures, so that changes will be visible the next time the graphic is drawn. We could get similar results by setting the property ICachedGraphic::IsDirty, but then it would call Refresh during our drawing loop, slowing it down.
  16. The utility functions addRefresh and resetRefresh are used to build a clipping envelope for IActiveView::PartialRefresh. The envelope is stored in m_ipRefreshEnvelope. With a few exceptions, these functions are used anywhere a refresh is needed. Note that in most of the update sequences throughout the code, resetRefresh is first called using the previous extent of the selected graphic, and then addRefresh is called once the new extent is available. This is done any time the extent of the selected graphic changes.
  17. The utility function computeSize calculates the geographic height of a force element in geographic units based on the current position of the size slider. It is used when a new force element is placed on the map, and whenever the slider is moved.
  18. The four copyXXXToXXX utility functions are used to move string values back and forth between the selected graphic and the text editor widgets in the control panel. Whenever a force element is selected on the map, the copyXXXToText functions are called. Whenever the text in the control panel is edited, the copyTextToXXX functions are used.
  19. In copyPropToText and copyTextToProp, we use the PropertySet property on the IForceElement interface associated with the selected graphic to set and retrieve label text. Each key-value pair in the property set corresponds to an attribute in a MOLE feature. The key is the field name, and the value is the attribute value, which is used to label the force element.
  20. In copySICToText and copyTextToSIC, we use different properties in each direction. (There are sometimes multiple ways to do something.) Going from the graphic to the text box, we use the SymbolID property on the IFEGraphic interface of the selected graphic. This property is based on the MessageString property of the ForceElement property when the graphic is refreshed. Going from the text box to the graphic, we use the MessageString property on the IForceElement interface, to ensure that all objects in the graph are consistent.
  21. Only four events from IMapControlEvents2 are used. The OnAfterDraw event is used to draw the force elements on the map. The foreground phase, normally unused in a map, is used to draw the force elements with no display caching. The lack of caching keeps the update fast and simple, but leaves a "flicker" on the display that reveals the clipping extent. There are several calls to IActiveView::PartialRefresh throughout the code, all specifying the foreground phase, which triggers the drawing code. Drawing is accomplished by calling ICachedGraphic::Draw on each force element, followed by a Highlight call on the selected graphic, if any. Clipping is done automatically by the drawing framework.
  22. The three mouse events are used to place and move force elements on the map. The OnMouseMove event implements the dragging functionality by applying the mouse movement vector to the geometry of the selected graphic via the IForceElement::Shape property. The OnMouseUp event simply clears a flag that lets OnMouseMove know that it no longer needs to move the selected graphic.
  23. Most of the mouse-related work is in OnMouseDown, which consists of two large blocks: one to place a new force element, and one to change the selected graphic. The placement block checks the symbol ID and provides a couple of error messages, although it cannot detect all types of malformed codes – only MOLE can do this. Placement begins by instantiating a ForceElement and its point geometry, and calls IForceElement2525BRenderer::GraphicByForceElement to create the cached graphic. The label and symbol ID code of the new force element are taken from the current contents of the text boxes, and its size and rotation are based on the current slider settings.
  24. The block in OnMouseDown that selects a graphic calls ICachedGraphic::HitTest on each one. The tests are made in the same order that the force elements are drawn, so the last successful hit test is the one that is used for the new selection. The new selection is moved to the back of the list to ensure that it draws on top of the others, and its symbol ID and selected label (if any) are copied to the control panel.
  25. There is one Qt slot function for each control panel widget that we actively respond to. All except the push button slots are named changeXXX, and get called each time the user changes the widget’s value. In changeMode, we do the basic housekeeping associated with the modes. In changePropName, we get the newly selected label’s value from the selected graphic and copy it into the text field. In changePropText, the user has edited the label selected in the combo box. When in edit mode, we apply the new text to the selected graphic and refresh its extent, giving the appearance that the labels are changing in unison with the text field. In clearFE, we delete all the force elements and redraw the entire map. In deleteFE, we delete the selected graphic and redraw its extent. (There is always a selected graphic in this case because the button is disabled when there is not.) In randomizeSIC we call the random symbol ID generator in Symbols.cpp. If in edit mode, we apply the new symbol ID to the selected graphic and refresh its extent, giving the appearance that the force element is randomly changing in unison with the button click.
  26. In changeAngle, we convert the slider value (degrees) to radians and call ICachedGraphic::set_Angle on each force element. After that, we refresh the entire map, giving the appearance that all the force elements are rotating in unison with the slider. In changeSize, follow a similar process using ICachedGraphic::set_Size, giving the appearance that all the force elements are growing and shrinking in unison with the slider.
  27. In changeFEType, we alter the second character of the symbol ID code, which indicates affiliation, in the text widget. When in edit mode, the modified symbol ID code is set immediately in the selected graphic, giving the appearance that its shape and color are changing in unison with the radio buttons. The same operation is used in changeSICText any time the user edits the symbol ID directly. In this case you can see in the code that each edit to the symbol ID box causes the string from that box to be set in the selected graphic. However, the actual symbol displayed will not change except when the symbol ID code in the box is valid.
  28. In exportFE, we begin by constructing an export file name for each force element on the map. To incorporate the symbol ID code in the file name, we call get_SymbolID on the IFEGraphic interface of the cached graphic. What follows that is a for-loop and some conditional statements that attempt to find the ratio of the symbol’s frame height to the "unit" frame height described in the 2525B Change 1 standard. This is done by looking up certain characters in the symbol ID code in strings stored in a static array that is defined at the top of the file. We decide how many image pixels are in the "unit" frame height by assigning a value to the unitFrameHeight constant at the top of this function. We then do a little math with the heights of the graphic and frame geometry extents to decide how many pixels high the image should be. After that, an ExportToFile call on the IExportGraphic interface of the cached graphic creates the image file. To make this call we need a reference to the map display, the file name, the image height, and a pointer to hold the result flag. The remaining arguments are safe defaults.

DrawForceElements.cpp Implementation file
DrawForceElementsWindow.cpp Implementation file
Symbols.cpp Implementation file
DrawForceElements.h Header file
DrawForceElementsWindow.h Header file
Makefile Unix Makefile
NMakefile.mak Windows Makefile
Download the QT_CPP files

Download the files for all languages




Development licensing Deployment licensing
Engine Developer Kit: Military Overlay Editor Engine Runtime: Military Overlay Editor