Latitude/Longitude Class

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.

Click here for larger image

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:

Click here for larger image

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 backspace key.

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 controls.


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 controls.


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_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.

UINT GetStyle();
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 GC_DECIMAL_DEGREES_STYLE.

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 GC_DECIMAL_DEGREES_STYLE.

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.

String Resources

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:


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:

IDS_DEFLATOPT "Reset Latitude"
IDS_DEFLONOPT "Reset Longitude"
IDS_RESETHEM "Reset Hemisphere"
IDS_RESETDEG "Reset Degrees"
IDS_RESETMIN "Reset Minutes"
IDS_RESETSEC "Reset Seconds"

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:

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 controls.

The Demo

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:
  1. Add CGCColorEdit class to your project.
  2. Add CGCMultiFieldEdit class to your project.
  3. Add CGCLatLongAbstractBase class to your project.
  4. Add CGCLatitudeCtrl class to your project.
  5. Add CGCLongitudeCtrl class to your project.
  6. Add CGCContextMenu class to your project.
  7. Place an edit box using the Resource Editor to be used for your latitude control.
  8. Place an edit box using the Resource Editor to be used for your longitude control.
  9. Use ClassWizard, create a member variable of type CGCLatitudeCtrl for one of the new edit boxes.
  10. Use ClassWizard to create a member variable of type CGCLongitudeCtrl for the other new edit box.
  11. 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.


Download self extracting demo and source code - 304 Kb


  • Great goods from you

    Posted by multiertEmoto on 02/05/2013 10:06pm

    I precisely wished to thank you very much all over again. I am not sure what I might have worked on in the absence of these [url=]sac longchamp[/url] strategies shared by you over that subject matter.[url=]longchamp sac[/url] sac longchamp ,It absolutely was an absolute frustrating problem in my opinion, however , witnessing the specialised style you dealt with that forced me to jump over gladness. I'm thankful for your information and thus trust you are aware of an amazing job you happen to be getting into [url=]sac longchamp pliage[/url] sac longchamp pliage teaching sac longchamp longchamp sac many people through your site. I'm certain you've never met any of us.

  • Rounding problems when setting decimal degrees

    Posted by srbrook on 09/30/2004 10:22am

    When setting a value such as -7.3 I get a rounding problem that would result in trying to set 07:17:60.0 resulting in a SAVESEC crash.
    If the seconds are rounded up from 59.99999 to 60.0 then the seconds should be set to 0 and the other fields adjusted to compensate. This results in the following changes to CGCLatLongAbstractBase::SetDecimalDegrees
    		// Call utility function to break down decimal degrees value.
    		if (IsStyle(GC_FLOAT_SECONDS_STYLE,m_Style))
    			// Round up as needed.
    			// Since the GC_FLOAT_SECONDS_STYLE supports a seconds field
    			// that provides precision to 1/1000th of a second, I will
    			// multiply by 1000 and round up.  Then use the floor() function
    			// to throw away garbage values.
    			Seconds = TruncDblSeconds(Seconds);
    			if (Seconds>m_maxSeconds)
    				Seconds = 0;
    				if (Minutes>59)
    					Minutes = 0;
    		_SAVEDEG(Degrees);		// Store degrees
    		_SAVEMIN(Minutes);		// Store minutes

  • _SAVEDEG crash

    Posted by Legacy on 06/11/2003 12:00am

    Originally posted by: Chris


    Ive used this control in the apst and it works fine however Ive just added it to a new program and I get a crash when I call SetDecimalDegrees(0.0); Through debug Ive found the crash occurs when the _SAVEDEG macro is called.

    Any ideas as to what could be causing it - its driving me up the wall!

    Thanks in advance

  • How to Digitize a Map??? (Topographic)

    Posted by Legacy on 11/29/2001 12:00am

    Originally posted by: Amit Gefen

    Is someone know how to describe a topographic mac in a file?

  • CGCNumEdit Control

    Posted by Legacy on 11/20/2001 12:00am

    Originally posted by: Joe Bernier

    I needed time in hh:mm:ss.000000. Your class was very quick and easy to modify. Thanks!

  • Tell Me How You Use These Controls

    Posted by Legacy on 08/09/2001 12:00am

    Originally posted by: George Chastain

    I would like to know how you are using these controls. Don't give out proprietary or classified information but let me know as best you can how you are using these controls. I would really like to know. They have proven very popular.


  • Also, see these articles...

    Posted by Legacy on 05/23/2001 12:00am

    Originally posted by: George Chastain

    If you found this article interesting, you may want to see "UTM Coordinate Control" at:

    If you want to learn more about the CGCMultiFieldEdit class, see the article "Multi-Field Edit Controls -- A Whole New Class of Edit Control" at:

    Catch me on the web at:

  • Source Code Updated

    Posted by Legacy on 05/22/2001 12:00am

    Originally posted by: George Chastain

    The source code has been updated.

    I corrected a small error that another reader (Tero) caught in the source code that is used to initialize the latitude/longitude control to some value. I also found that the last update was missing a needed preprocessor directive that would keep the context menu from functioning. The project settings have been updated for this. Also note that I have *some* code in place to support hard-coded strings for the context menu but not all code is there to support it. I didn't bother finishing it because I thought that the resource table was the best method. This does not interfer with the correct operation of the controls as implemented. However, if you feel the need to complete that implementation, feel free to do so. The resource table method makes it easier to make changes to support other languages.

    I have received a lot of great feedback on this code as well as the code accompanying my article on "Multi-Field Edit Controls -- A Whole New Class of Edit Controls" and a related control class, my "UTM Coordinate Control".

    Thanks for the feedback. I hope to have time soon to contribute more articles to this site.

    Catch me on the Net at my personal home page:


  • Thanks Tero. An editorial error

    Posted by Legacy on 05/10/2001 12:00am

    Originally posted by: George Chastain

    Thanks. I did not catch that. The function is used when initializing a value and I was concentrating on getting other code to work that I overlooked this. It is clearly and editorial error since I set the variable "result" to one thing in the line before and then set to something else in the line in question. I will submit and update soon.

  • Truncation bug

    Posted by Legacy on 05/10/2001 12:00am

    Originally posted by: Tero Molander

    There's a bug in CGCLatLongAbstractBase::TruncDblSeconds function.

    double CGCLatLongAbstractBase::TruncDblSeconds(const double Seconds)
    // This must be kept consistent with the parameters passed to the
    // CGCDoubleField constructor for the floating-point seconds field
    // support object (see private method DefineFields()).
    double result = 0.0;
    int factor = 1 * int(pow(10.0,double(FLTSECPRECISION)));
    result = Seconds * factor + 0.5;
    result = floor(Seconds);
    result /= factor;
    return result;

    The problem lies in result = floor(Seconds); line. If seconds were f.ex. 59.0 you would get 0.059 as a result. Correct line would be result = floor(result);. Works for me anyway.

  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Ever-increasing workloads and the challenge of containing costs leave companies conflicted by the need for increased processing capacity while limiting physical expansion. Migration to HP's new generation of increased-density rack-and-blade servers can address growing demands for compute capacity while reducing costly sprawl. Sponsored by: HP and Intel® Xeon® processors Intel, the Intel logo, and Xeon Inside are trademarks of Intel Corporation in the U.S. and/or other countries. HP is the sponsor …

  • Download the Information Governance Survey Benchmark Report to gain insights that can help you further establish business value in your Records and Information Management (RIM) program and across your entire organization. Discover how your peers in the industry are dealing with this evolving information lifecycle management environment and uncover key insights such as: 87% of organizations surveyed have a RIM program in place 8% measure compliance 64% cannot get employees to "let go" of information for …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds