I had developed two control classes that would provide for the consistent entry and display
of latitude and longitude values. Recently I developed a powerful abstract (pure virtual)
class for a new category of edit controls I refer to as Multi-Field Edit Controls. That class,
CGCMultiFieldEdit, has proven to be very popular. I have also found the class to be very useful
in creating new controls and exceptionally easy in adding new behavior and styles to existing
controls derived from it. I wanted to take advantage of this new versatile class in the
implementation of my popular latitude and longitude control classes. Therefore, I have
redesigned my Latitude and Longitude control classes to use CGCMultiFieldEdit. In addition,
I have added a set of styles that may be applied to the Latitude and Longitude controls to
make them more versatile.
The demo program, shown below, illustrates the capabilities of these classes.
Reflections on Design
I had a difficult time deciding just what would be the best way to redesign the Latitude
(CGCLatitudeCtrl) and Longitude (CGCLongitudeCtrl) classes. I had developed my original
CGCLatitudeCtrl and CGCLongitudeCtrl classes in a hurry and they duplicated most of the
code in the classes. I wanted to eliminate that duplication and make them more “object
oriented.” If I were to be a fanatical OOP purist, I should put the code specific to the
latitude control in the CGCLatitudeCtrl class and the code specific to the longitude control
in the CGCLongitudeCtrl class. However, the differences in implementation between the two
are only a few lines of code. The maximum value of the degree field and the way in which
you represent the hemisphere for certain styles are the only differences. And, since it is
extremely unlikely that you would ever have a need for JUST a latitude or JUST a longitude
control, you should make sure that both controls always use consistent formats and behavior.
In order to emphasize the need for consistency across both control classes, and due to the
small difference between their implementations, I opted to develop an abstract (pure virtual)
class, CGCLatLongAbstractBase, that would pull the implementation for both controls together
into a single class. I would then provide two control classes, CGCLatitudeCtrl and
CGCLongitudeCtrl derived from the new abstract class, that would provide only specialized
input validation for their methods and maybe a few methods to make better use of the abstract
class’ methods. This arrangement allowed me to provide the tight relationship needed in their
implementation (to ensure consistency) while emphasizing the fact that they are still logically
different, albeit related, entities.
When creating Multi-Field Edit Controls such as CGCLatitudeCtrl and CGCLongitudeCtrl using the
CGCMultiFieldEdit class, the implementation is typically fairly compact. However, if your
control needs to support a number of different styles for different customer needs as this
implementation required, then the implementation can become more complicated. But then that’s
what happens when deriving a new class from an existing one. As the class requires more
functionality, it can become more complicated.
The implementation file for CGCLatLongAbstractBase is long. For better manageability, it
should be broken up into smaller files. However, I wanted to make incorporating it into
a new project as easy as possible so I put all the code in one file.
I had a lot of positive feedback on the last design and I hope that you will view this one
in a positive manner as well.
The class hierarchy for the Latitude and Longitude classes is illustrated in the following figure:
The figure above also shows that I indirectly derived the latitude and longitude
controls from my CGCColorEdit class to provide a means for controlling the colors and fonts
used in the controls. Since there are already articles available on how to control color
and fonts in edit controls, I will not dwell on the use of that class. However, you may
find my implementation of CGCColorEdit to be very versatile.
The CGCLatLongAbstractBase class also makes use of a new class that I created to ease the
implementation of the context menus for the controls. This new class, CGCContextMenu,
provides some wrappers for convenience. Feel free to add to this class as you wish.
Preprocessor Flags and Compiler Switches
The latitude and longitude controls now support several styles to be described below.
Among these is a style that supports the display and entry of a decimal degrees value.
The implementation makes use of a preprocessor constant “GCDD” to conditionally compile
support for the decimal degrees style. If you will not need to support the display/entry
of decimal degrees, do not define GCDD. That code will then be left out upon compilation.
However, if you need support for decimal degrees, be sure to define GCDD in your compile
options. There is no way to compile only decimal degrees support. You will always get
support for degrees-minutes-seconds regardless of compiler options.
An alternative to this approach would have been to derive extended CGCLatitudeCtrl and
CGCLongitudeCtrl classes from the existing ones that would have added the decimal degrees
functionality. Using a preprocessor flag was just the choice I went with so I wouldn’t
have to keep track of as many classes.
Because of the use of logically different field support classes in the implementation
of the controls, some dynamic casting was used to access the appropriate methods for the
different field support objects. This requires that the project using these classes
be compiled with the /GR switch to enable Run-Time Type Information (RTTI).
The CGCLatitudeCtrl and CGCLongitudeCtrl classes are very simple to use. Each control divides
its display into different fields. The user can navigate between fields within the control
using the left and right arrow keys. Navigation to the right may also be achieved using the
space bar. When the user navigates to a particular field within the control, that field is
selected and highlighted. Actions taken by the user will affect that field. The up and down
arrow keys may be used to increment or decrement, respectively, the currently selected field.
In addition, the user may enter values for that field directly using the number keys on the
keyboard. The user may also select a specific field within the control by clicking on it
with the left mouse button. The user can reset the currently-selected field by pressing the
A context menu is even provided. When the user clicks over the control that has focus using the
right mouse button, a context menu is displayed. It allows the user to reset the entire control
setting or to reset only the currently-selected field. There are even “hot keys” defined for
The new implementation provide several styles that may be applied to allow the developer
to tailor these controls to most needs. The controls provide the following styles:
This style specifies that colons are to be used to separate the fields in both controls.
This style specifies that a single white space is to be used to separate the fields in both
This style specifies that both controls are to use a contemporary format in the representation
of a latitude or longitude. In this style, the raised “degree symbol” is used after the degree
field, a single quote is used after the minutes field and a double quote is used after the
seconds field. A single white space is used to separate the hemisphere field from the latitude
or longitude value.
This style allows the client to specify that strings obtained from the string resource
table are to be used after each field. The string resources used are identified as
IDS_CUSTOM_HEM_DELIM, IDS_CUSTOM_DEG_DELIM, IDS_CUSTOM_MIN_DELIM and IDS_CUSTOM_SEC_DELIM.
For further details, see “STRING RESOURCES” below.
This syle states that the string resources IDS_NHEM_TXT and IDS_SHEM_TXT are to be used
to indicate the hemisphere in the Latitude Control and the string resources
IDS_EHEM_TXT and IDS_WHEM_TXT are to be used to indicate the hemisphere in the Longitude
Control. For further details regarding these resources, see “STRING RESOURCES” below.
This style states that the string resources IDS_POSHEM_TXT and IDS_NEGHEM_TXT are to be
used to indicate the hemisphere in both controls. For further details on these resources,
see “STRING RESOURCES” below.
Use a floating point seconds field in both controls. The seconds field represents seconds
to the thousandths.
Use a simple integer seconds field for both controls.
Specifies that leading zeros be used in all numeric fields in both controls.
Specifies that no leading zeros be used in any field in both controls.
Specifies that the hemisphere indicator is to be displayed on the left of the control.
Specifies that the hemisphere indicator is to be displayed on the right.
Specifies that the control display the latitude/longitude as separate hemisphere, degrees, minutes and seconds values.
Specifies that the control display the latitude/longitude using the hemisphere and the decimal degrees value as two separate fields. This style is not defined if the code is not compiled with “GCDD” defined.
Equivalent to OR’ing the GC_COLON_DELIM_STYLE, GC_HEM_INDICATOR_STYLE, GC_FLOAT_SECONDS_STYLE, GC_LEADING_ZEROS_STYLE, GC_FULL_STYLE and GC_LEADING_HEM_STYLE styles.
Equivalent to OR’ing the GC_COLON_DELIM_STYLE, GC_HEM_INDICATOR_STYLE, GC_LEADING_ZEROS_STYLE GC_DECIMAL_DEGREES_STYLE and GC_LEADING_HEM_STYLE styles. This style is not defined if the code is not compiled with “GCDD” defined.
In this demo, the client sets the background and text colors, as well as selecting a non-
proportional (fixed pitch) font using methods provided by the CGCColorEdit class. I find that
Multi-Field Edit Controls, such as CGCLatitudeCtrl and CGCLongitudeCtrl look better when using
a non-proportional font.
There are several methods defined for each class in addition to those inherited from their ancestor
classes. These are:
BOOL SetStyle(const STYLE_TYPE Style);
The SetStyle() method is used to set the controls to the style(s) defined above. The method
allows the client to specify only the style to be set without the client needing to OR all
desired styles together.
Returns the current style of the control.
BOOL Set(const CString& hsph, const UINT Degrees, const UINT Minutes, const float Seconds);
The method Set() allows the application to initialize the control to any setting valid for
that type of control (latitude or longitude). With this method, the client may specify the
latitude or longitude using separate hemisphere, degrees, minutes and seconds values. The
method returns TRUE if successful and FALSE if any parameter is outside the valid range for
that parameter. This method may be for controls with either the GC_FULL_STYLE or the
void Get(CString& hsph, UINT& Degrees, UINT& Minutes, float& Seconds) const;
The method Get() allows the application to obtain the current setting as separate values.
This is usually called from within an EN_CHANGE handler. This method may be called for
controls with either the GC_FULL_STYLE or the GC_DECIMAL_DEGREES_STYLE.
double GetDecimalDegrees() const;
The method GetDecimalDegrees() allows the client to obtain the current setting in decimal
degrees. Like method Get(), this method would typically be called from an EN_CHANGE handler.
This method may be called for controls with either the GC_FULL_STYLE or the
BOOL SetDecimalDegrees(const double Degrees);
The SetDecimalDegrees() method allows the client to initialize the control by specifying
a decimal degree value. Returns TRUE if if the call succeeds or FALSE if the call fails
due to an invalid input parameter. This method may be called for controls with either the
GC_FULL_STYLE or the GC_DECIMAL_DEGREES_STYLE.
For further details on these methods and others that the control classes inherit, please
refer to the extensive comments supplied in the source code of the demo project.
The CGCLatLongAbstractBase class makes use of string resources to facilitate modifications
for different languages and for custom needs.
There are 6 string resources used to support two different styles controlling the
use of the hemisphere field. These are:
|RESOURCE ID||VALUE||USED BY STYLE|
They are defined to the typical characters one would expect to use when
indicating/specifying a hemisphere. However, you are not restricted to using single-character
strings. You could, for example, specify “NORTH” for IDS_NHEM_TXT and “SOUTH” for IDS_SHEM_TXT.
However, the design of the CGCMultiFieldEdit class imposes a field width constraint on the
contents of a given field via parameters specified in the CGCMultiFieldEdit::Format() method
call. As the field changes value, its width stays the same. Therefore, you should ensure that
the strings you specify for IDS_NHEM_TXT and IDS_SHEM_TXT are the same length. Further, you
should ensure that the strings for IDS_EHEM_TXT and IDS_WHEM_TXT are the same size and the same
is true for IDS_POSHEM_TXT and IDS_NEGHEM_TXT. For example, if you wished to do something silly
like use “UP” to represent the Northern hemisphere and “DOWN” to represent the Southern
hemisphere, then you should define IDS_NHEM_TXT as “UP ” and IDS_NHEM_TXT as “DOWN”.
Otherwise, “DOWN” will likely be displayed as “DO”! For further details on the use of the
CGCMultiFieldEdit class, please see my article “Multi-Field Edit Controls – A Whole New
Class of Edit Controls”.
There are 6 string resources that are defined for use in the control context menus. These are:
These allow the developer to change the strings used for these menu options if needed.
A private method of CGCLatLongAbstractBase, LoadStringResource() provides a convenient
location for modifications should you need to change how the menu options are defined/obtained.
There are 4 string resources defined to support the GC_CUSTOM_DELIM_STYLE style.
These are labeled as follows:
|RESOURCE ID||VALUE||RESOURCE USE|
|IDS_CUSTOM_HEM_DELIM||” h ”||Displayed following the|
|IDS_CUSTOM_DEG_DELIM||” d ”||Displayed following degress field|
|IDS_CUSTOM_MIN_DELIM||” m ”||Displayed following minutes field|
|IDS_CUSTOM_SEC_DELIM||” s ”||Displayed following seconds field|
The strings provided in the resource table for these resource IDs are examples only. If you
wish to use the GC_CUSTOM_DELIM_STYLE, you may change the definitions of these resources to
whatever you wish. These string resources are provide to allow you to customize a style for
The demo supplied provides you with controls to apply the different styles to the Latitude and
Longitude controls. You can combine the different styles and see how they interact. The demo
also illustrates a possible use for the CGCColorEdit class functionality for your latitude and
longitude controls. In this demo, the background color changes according to the hemisphere
being displayed! You probably will not want this but it illustrates one use of the CGCColorEdit
class. The primary reason for using CGCColorEdit is to select a non-proportional (fixed pitch)
font for the controls. Such a font makes the controls look better when changing the field
values because the fields do not shift left and right as different width characters are displayed.
How to Use
Here are the steps to place a latitude control and a longitude control in your user interface:
- Add CGCColorEdit class to your project.
- Add CGCMultiFieldEdit class to your project.
- Add CGCLatLongAbstractBase class to your project.
- Add CGCLatitudeCtrl class to your project.
- Add CGCLongitudeCtrl class to your project.
- Add CGCContextMenu class to your project.
- Place an edit box using the Resource Editor to be used for your latitude control.
- Place an edit box using the Resource Editor to be used for your longitude control.
- Use ClassWizard, create a member variable of type CGCLatitudeCtrl for one of the new edit boxes.
- Use ClassWizard to create a member variable of type CGCLongitudeCtrl for the other new edit box.
- Add an EN_CHANGE handler for each of the new edit boxes to your parent window to handle when the controls change as you would any other control class.
Also see my related article “UTM Coordinate Control” on a control for entering and displaying
Universal Transverse Mercator coordinates.
I hope that these classes will continue to prove useful to someone on future projects.