A cross-platform geolocation information application in C++
clinic-answer-geolocation-cpp
Geolocation Information Desktop Application
Example answer to the Where Am I geolocation challenge of the the LinkedIn Learning: Code Clinic: C++.
Note that this is not my first wxWidgets C++ application. The learning curve was low; and wxPython 4 was one tool used to prototype the application in Python before implementing in C++.
Derived requirements:
- The application should be cross-platform between Windows 10 Pro 20H2, Ubuntu 2018.04 LTS, and macOS 10.15 Catalina.
- The application should be possible to distribute closed-source by not relying upon a copy-left graphical toolkit or networking library.
- The application should print the external IP address, estimated Latitude and Longitude, and estimated street address of the estimated Latitude and Longitude. An estimated bounding-box is not calculated as the author could not locate a Maps service that was as comprehensive as Google Maps, with Wifi routers; and the author was unwilling to submit a credit card to the geolocation service.s
- The application should state the geolocation information on the window in plain text that can be copy-pasted.
Refined requirements
- The application shall use the wxWidgets 3.0.x/3.1.x graphical toolkit for the design of the window frame and controls.
- The application shall use Boost C++ 1.65 or newer for any beyond-STL functionality required.
- The application shall use Nlohmann JSON C++ headers 2.1 (Ubuntu) or 3.9.1 (macports or Windows) for the parsing of JSON data.
- Examples in the blog post shall only give hints.
- Examples in the blog post shall not disclose the geolocation results. This complies with common sense as well as the User Policies of the geolocation APIs utilized.
Example screen shots of the completed application:
Geolocation Information App on Ubuntu 18.04 LTS.
Geolocation Information App on Windows 10 Pro 20H2.
Geolocation Information App on macOS Catalina.
Hints for cross-platform design
Example code structure for in-memory fetch of a HTTP/HTTPS web page with libcurl
A full example of requesting a HTTP/HTTPS web page and storing in RAM on the heap can be found at: https://curl.se/libcurl/c/getinmemory.html .
The full example is more comprehensive than the partial code snippet found in the API reference at: https://curl.se/libcurl/c/curl_easy_setopt.html#EXAMPLE .
Note also that after adapting the full example to a C++ method, the
struct MemoryStruct chunk;
declaration in the full example should be declared static as
static struct MemoryStruct chunk;
to prevent any accidental callback to a deallocated stack variable from the CURL library, once the containing C++ method has returned.
Example debugging with nlohmann JSON for Modern C++
The nlohmann JSON source can be used as a single C++ header include.
Note that the return type of nlohmann::json::parse()
is a nlohmann::json
object. If the object is subscripted accordinig to associative array style,
the C++14 compiler may not enforce the return type for the left-hand of the
assignment. For example, where querying a reverse?
query at
https://nominatim.openstreetmap.org/
, the return is a JSON text document.
In my code I query the “display_name” for the full address with city and zip
code, of the approximated map location of my computer. In C++, this looks like:
std::string calcStr("");
nlohmann::json calcData;
...
calcStr = calcData["features"][0]["properties"]["display_name"];
If the JSON document is not formatted the same as the code’s assumption of the four levels, an exception is raised. These statements should be inside a try{} catch(){} block.
Example using a non-Google, no cost, Geolocation on the Web
Steps:
- Query
https://api.ipify.org
. - Query
http://api.ipstack.com/
specifying IP address and free access key. - Query
https://nominatim.openstreetmap.org/reverse?
specifying Latitude and Longitude coordinates. The CURLOPT_USERAGENT must be specified with a call tocurl_easy_setopt()
. Otherwise, the service will block the query.
Example compiling on Ubuntu with clang++
apt-get install clang-10 \
wx3.0-headers libwxgtk3.0-dev libwxgtk3.0-gtk3-dev \
libboost-all-dev nlohmann-json-dev
#!/bin/bash
mkdir -p ./Debug/
clang++-10 *.cpp \
$(wx-config --cxxflags --libs) \
-lcurl -std=c++14 -g -O0 \
-o Debug/GeolocationApp.elf
Example running on Ubuntu once the program is compiled
#!/bin/bash
./Debug/WeatherApp.elf
Compiling with Xcode 12.3 on macOS 10.15
port install curl nlohmann-json
- Re-use the minimal_cocoa Xcode project from the wxWidgets-3.1.4 samples/ directory (from the full downloaded wxWidgets source). Configure to compile wxWidgets as part of the Xcode project hierarchy.
- Add C++ sources to src/ .
- Add macports /opt/local/include/ to header paths.
- Have the Xcode subproject build the app-dynamic target with -std=c++14 or -std=gnu++14 .
- Embed & Sign the resulting wxWidgets .dylib into the Xcode application bundle.
- Link (without embedding) the curl .dylib into the Xcode application bundle. This is accomplished by adding an entry for libcurl.4.dylib under Build Phases -> Link Binary with Libraries .
Example running on macOS 10.15 once the program is compiled
- Select the menu: Product -> Run
Example compiling on Windows 10 with Visual Studio 2019
-
Bulid libcurl from source. I followed: https://medium.com/@farhabihelal/how-to-set-up-libcurl-on-visual-studio-2019-a9fdacce6945 .
- Add
..\curl\builds\libcurl-vc-x86-release-static-ipv6-sspi-schannel\lib\libcurl_a.lib
to Linker Additional Dependencies. - Add the followinig to Linker Additional Dependencies: Ws2_32.lib , Wldap32.lib , Crypt32.lib , Normaliz.lib .
- Install boost 1.74.0 source.
- Install the nlohmann JSON for C++ 3.9.1 source.
- Install wxWidgets 3.1.4 source and binaries. For the binaries, separate the .lib files into a
_lib
directory and the .dll files into a_dll
directory. - Add the
C:\wxWigets-3.1.4\lib\vc14x_x64_dll
directory to the system PATH variable. - In Visual Studio 2019, properties to set on the project:
- In Property Manager, add
C:\wxWidgets-3.1.4\wxwidgets.props
to the execution configurations of the project. - In VC++ Directories:
- Add
C:\wxWidgets-3.1.4\lib\vc14x_x64_lib
as a Library Directory
- Add
- In C/C++ All Options:
- Add
C:\boost_1_74_0
,C:\nlohmann-json\single_include
to Additional Include Directories - As needed, select from the following for Preprocessor Definitions:
WIN32;DEBUG;_DEBUG;_WINDOWS;__WXMSW__;__WXDEBUG__;_UNICODE;WXUSINGDLL; wxMSVC_VERSION_ABI_COMPAT;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE; _CRT_NONSTDC_NO_DEPRECATE;
- Add
- In Linker All Options:
- Add
$(wxRootDir)\lib\$(wxCompilerPrefix)$(wxArchSuffix)_lib;$(wxLibOrDllDir);
to Additional Library Directories.
- Add
- In Property Manager, add
Example running on Windows 10 once the program is compiled
- Select the menu: Debug -> Run