Google Maps API Example ======================= Overview -------- Google Maps is a popular service to search and retrieve travel information. Besides the usual [maps.google.com](https://maps.google.com) Google Maps Web site, Google also provides REST APIs for their services. These REST APIs can be used to develop applications that can search and retrieve real-time travel information. This document gives two example C++ client applications for two popular Google Maps APIs, the [Google Maps Distance Matrix API](https://developers.google.com/maps/documentation/distance-matrix/start) and the [Google Maps Directions API](https://developers.google.com/maps/documentation/directions/start). The applications are implemented with gSOAP and use REST with XML or REST with JSON. To use a Google Maps API you must obtain an API key. The APIs are also subject to API policies and usage quotas, which may differ per API. The API keys and quotas will be explained later in this document in more detail. [![To top](../../images/go-up.png) To top](#) ### Google Maps Distance Matrix API The [Google Maps Distance Matrix API](https://developers.google.com/maps/documentation/distance-matrix/start) is a service that provides travel distance and time for a matrix of origins and destinations. The first example in this document explains the implementation of a C++ client application `gdm` for the Google Maps Distance Matrix API to search for travel distance and time for a matrix of origins and destinations. The API returns a matrix of distances and time for each pair of origin and destination. You can specify the mode of transportation, unit (metric or imperial), origin addresses, and destination addresses as as text strings or as latitude/longitude coordinates, or as place IDs. The Google Maps API provides additional parameters for search options, which are not used by this example application but you can add these easily to the application's source code. The `gdm` application is executed from the command line. For example, to find all pair-wise bicycling distances (in meters) and times (in seconds) from Vancouver BC or from Seattle to San Francisco or to Vancouver BC: [command] ./gdm <YOUR_API_KEY> bicycling imperial 'Vancouver BC|Seattle' 'San Francisco|Vancouver BC' ---------------------------------------- from: Vancouver, BC, Canada to: San Francisco, CA, USA duration: 326774 (3 days 19 hours) distance: 1.70553e+06 (1,060 mi) to: Vancouver, BC, Canada duration: 0 (1 min) distance: 0 (1 ft) ---------------------------------------- from: Seattle, WA, USA to: San Francisco, CA, USA duration: 274759 (3 days 4 hours) distance: 1.45204e+06 (902 mi) to: Vancouver, BC, Canada duration: 55726 (15 hours 29 mins) distance: 271157 (168 mi) ---------------------------------------- To use the Google Maps Distance Matrix API you need an API key `YOUR_API_KEY`. Register your project and [get an API key.](https://developers.google.com/maps/documentation/distance-matrix/get-api-key) Jump to [XML Google Maps Distance Matrix API with gSOAP](#gdmapi) to start with the Google Maps Distance Matrix XML REST API or jump to [JSON Google Maps Distance Matrix API with gSOAP](#json-gdmapi) to start with the Google Maps Distance Matrix JSON REST API. [![To top](../../images/go-up.png) To top](#) ### Google Maps Directions API The [Google Maps Directions API](https://developers.google.com/maps/documentation/directions/start) is a service that calculates directions between locations. You can search for directions for several modes of transportation, including transit, driving, walking, or cycling. The example in this document explains the implementation of a C++ client application `gdx` for the Google Maps Directions API to search for directions for several modes of transportation, including transit, driving, walking or cycling. The API returns multi-part directions using a series of waypoints. You can specify the mode of transportation, unit (metric or imperial), origin address, and destination address as as text strings or as latitude/longitude coordinates, or as place IDs. The Google Maps API provides additional parameters for search options, which are not used by this example application but you can add these easily to the application's source code. The `gdx` application is executed from the command line. For example, to find public transit directions with distances (in meters) and times (in seconds) from Times Square to Battery Park in New York City: [command] ./gdx <YOUR_API_KEY> transit imperial 'Times Square,New York,NY' 'Battery Park,New York,NY' ---------------------------------------- summary: warning: Walking directions are in beta. Use caution – This route may be missing sidewalks or pedestrian paths. copyrights: Map data ©2017 Google from Manhattan, NY 10036, USA to Battery Park, New York, NY 10004, USA takes 7386 meters (4.6 mi) 1531 seconds (26 mins) Walk to Times Sq - 42 St WALKING 551 meters (0.3 mi) 353 seconds (6 mins) Head <b>southeast</b> on <b>W 46th St</b> toward <b>Broadway</b> => WALKING 8 meters (26 ft) 6 seconds (1 min) Turn <b>right</b> onto <b>Broadway</b> => turn-right, WALKING 107 meters (351 ft) 81 seconds (1 min) Slight <b>right</b> onto <b>7th Ave</b><div style="font-size:0.9em">Destination will be on the left</div> => turn-slight-right, WALKING 318 meters (0.2 mi) 254 seconds (4 mins) => WALKING 118 meters (387 ft) 12 seconds (1 min) Subway towards South Ferry TRANSIT 6705 meters (4.2 mi) 1080 seconds (18 mins) take transit at 11:37am from Times Sq - 42 St to South Ferry Station arriving at 11:56am: => Subway Broadway - 7 Avenue Local line 1 heading South Ferry and exit at stop 12 (operated by MTA New York City Transit http://www.mta.info/) Walk to Battery Park, New York, NY 10004, USA WALKING 130 meters (427 ft) 100 seconds (2 mins) Head <b>north</b> on <b>Peter Minuit Plaza</b> => WALKING 17 meters (56 ft) 13 seconds (1 min) Turn <b>left</b> to stay on <b>Peter Minuit Plaza</b> => turn-left, WALKING 68 meters (223 ft) 51 seconds (1 min) Turn <b>left</b> onto <b>Battery Bikeway</b><div style="font-size:0.9em">Destination will be on the right</div> => turn-left, WALKING 45 meters (148 ft) 36 seconds (1 min) To use the Google Maps Directions API you need an API key `YOUR_API_KEY`. Register your project and [get an API key.](https://developers.google.com/maps/documentation/directions/get-api-key) Jump to [XML Google Maps Directions API with gSOAP](#gdxapi) to start with the Google Maps Directions XML REST API. [![To top](../../images/go-up.png) To top](#) XML versus JSON REST APIs {#xmlorjson} --- The Google Maps REST API services take HTTP GET requests and return XML or JSON responses, as specified with the GET request URL. The `gdm` and `gdx` example client applications use the Google Maps APIs with XML responses. This document also explains an implementation of an alternative JSON REST-based `json-gdm` application, see [JSON Google Maps Distance Matrix API with gSOAP](#json-gdmapi). An XML API is more practical and reliable in C++ code with gSOAP's [XML data bindings](https://www.genivia.com/doc/databinding/html/index.html) compared to a JSON API (in any C/C++ JSON implementation). An XML data binding offers the benefit of strong typing in C++ for reliable API coding. Strong typing means that XML elements are bound to specific members of a C++ class and the XML parser deserializes XML elements automatically into an instance of the class. Class member types are simply `std::string`, `double`, `enum`, another class to store subelements, or a `std::vector` to store arrays of elements. Deserializing XML into C++ with gSOAP assumes that an XML data binding interface header file is available. The soapcpp2 tool needs this interface header file to generate the XML data binding implementation code, including the XML (de)serializers that we want. Given a WSDL or XSD file we can simply use wsdl2h to auto-generate an XML data binding interface header file for us. A WSDL or XML schema serves as a contract between the Web service and the client applications that use the Web service. Unfortunately, no WSDL or XML schema is provided for Google Maps APIs. However, it is relatively easy to define the data binding interface header file manually based on the Google Maps API documentation. We define an interface header file from scratch based on the Google Maps API documentation of the XML REST APIs. The Google Maps API documentation is somewhat incomplete and requires some reverse engineering. Returned values are poorly documented with respect to the type of the values returned. Some elements, such as the Directions API *`<maneuver>`* element, are not documented at all. Some returned values are not documented, such as the *`establishment`* value of the *`<type>`* element in *`<geocoded_waypoints>`*. Some XML elements (or JSON objects) appear to be optional, guessing from the examples and descriptions, but it is not clear which elements are optionally returned. Numerical values appear to be integer, but JSON numerical values are unconstrained decimal according to the JSON "standard". We can assume with some certainty that the C/C++ `double` type will suffice to store numerical values returned by the APIs. The next sections explain the details of implementing C++ client applications for the Google Maps Distance Matrix API and the Google Maps directions API. The following source code files are referenced in this document and are downloadable: * [`GoogleDistanceMatrix.hpp`](GoogleDistanceMatrix.hpp) XML data binding interface header file for the Google Maps Distance Matrix API (XML REST API). * [`GoogleDirections.hpp`](GoogleDirections.hpp) XML data binding interface header file for the Google Maps Directions API (XML REST API). * [`gdm.hpp`](gdm.hpp) combines [`GoogleDistanceMatrix.hpp`](GoogleDistanceMatrix.hpp) and [`GoogleDirections.hpp`](GoogleDirections.hpp) into the `gdm` C++ namespace. * [`gdm.cpp`](gdm.cpp) the Google Maps Distance Matrix API client application * [`gdx.cpp`](gdx.cpp) the Google Maps Directions API client application The following source code files are generated with soapcpp2 from [`gdm.hpp`](gdm.hpp): [command] soapcpp2 -0 gdm.hpp * [`gdmStub.h`](gdmStub.h) a copy of the interface header file specification in plain C/C++ header file syntax without annotations. * [`gdmH.h`](gdmH.h) declares XML serializers. * [`gdmC.cpp`](gdmC.cpp) implements XML serializers. * [`gdmReadme.md`](gdmReadme.html) service and data binding interface details. The following source code files are generated with soapcpp2 from an empty [`env.h`](env.h) file: [command] soapcpp2 -0 -penv env.h * [`envStub.h`](envStub.h) a copy of the specification in plain C/C++ header file syntax without annotations. * [`envH.h`](envH.h) declares XML serializers. * [`envC.cpp`](envC.cpp) implements XML serializers. These last three files are needed when using a C++ namespace in interface header files for soapcpp2 to generate (de)serializers for empty SOAP Headers and SOAP Faults, because the gSOAP engine must still be linked with these SOAP Header and SOAP Fault structures, even when these are not used by our example applications. [![To top](../../images/go-up.png) To top](#) Using a C++ namespace for the APIs {#namespace} --- The [`gdm.hpp`](gdm.hpp) interface file combines the Google Maps API interface header files into one interface file and places all definitions within the `gdm` C++ namespace: namespace gdm { // add or remove APIs as needed #import "GoogleDistanceMatrix.hpp" #import "GoogleDirections.hpp" } Note the use of `#import` instead of `#include`. There are important differences between these two constructs. The soapcpp2 tool imports header files with `#import`, while `#include` is copied to the soapcpp2-generated header file so that C/C++ headers can be included in the generated source code. [![To top](../../images/go-up.png) To top](#) XML Google Maps Distance Matrix API with gSOAP {#gdmapi} --- The Google Maps Distance Matrix API returns information based on the recommended route between start and end points, as calculated by the Google Maps API, and consists of rows containing duration and distance values for each pair. A [Google Maps Distance Matrix API GET request URL](https://developers.google.com/maps/documentation/distance-matrix/intro#DistanceMatrixRequests) takes the following form: [command] https://maps.googleapis.com/maps/api/distancematrix/outputFormat?parameters where `outputFormat` may be either `json` or `xml`. To use the Google Maps Distance Matrix API you need an API key. [Get an API key.](https://developers.google.com/maps/documentation/distance-matrix/get-api-key) Review the [usage limits](https://developers.google.com/maps/documentation/distance-matrix/usage-limits) page for details on the quotas set for the Google Maps Distance Matrix API. Use of the Google Maps Distance Matrix API must be in accordance with the [API policies.](https://developers.google.com/maps/documentation/distance-matrix/policies) The `gdm` example client applications uses the Google Maps APIs with XML responses. For example, given "Vancouver BC" and "Seattle" as origins, "San Fransicso" and "Vancouver BS" as destinations, the following GET request computes the bycycling durations and distance for the four origin-destination pair combinations: [command] https://maps.googleapis.com/maps/api/distancematrix/xml?origins=Vancouver+BC|Seattle&destinations=San+Francisco|Vancouver+BC&mode=bicycling&units=imperial&key=YOUR_API_KEY This returns the following XML response by the API: [xml] <?xml version="1.0" encoding="UTF-8"?> <DistanceMatrixResponse> <status>OK</status> <origin_address>Vancouver, BC, Canada</origin_address> <origin_address>Seattle, WA, USA</origin_address> <destination_address>San Francisco, CA, USA</destination_address> <destination_address>Vancouver, BC, Canada</destination_address> <row> <element> <status>OK</status> <duration> <value>326774</value> <text>3 days 19 hours</text> </duration> <distance> <value>1705533</value> <text>1,060 mi</text> </distance> </element> <element> <status>OK</status> <duration> <value>0</value> <text>1 min</text> </duration> <distance> <value>0</value> <text>1 ft</text> </distance> </element> </row> <row> <element> <status>OK</status> <duration> <value>274759</value> <text>3 days 4 hours</text> </duration> <distance> <value>1452044</value> <text>902 mi</text> </distance> </element> <element> <status>OK</status> <duration> <value>55726</value> <text>15 hours 29 mins</text> </duration> <distance> <value>271157</value> <text>168 mi</text> </distance> </element> </row> </DistanceMatrixResponse> To use XML data bindings with gSOAP to parse this XML directly into C++ objects, we define an interface header file [`GoogleDistanceMatrix.hpp`](GoogleDistanceMatrix.hpp) as follows. The interface header file declares the *`DistanceMatrixResponse`* element that contains a *`row`* container with *`element`* containers. [![To top](../../images/go-up.png) To top](#) ### DistanceMatrixResponse Distance Matrix responses contain the following root elements: * *`status`* contains metadata on the request. * *`origin_address`* is an array of addresses as returned by the API from your original request. These are formatted by the geocoder and localized according to the language parameter passed with the request. * *`destination_address`* is an array of addresses as returned by the API from your original request. As with *`origin_addresses`*, these are localized if appropriate. * *`row`* is an array of elements, which in turn each contain a *`status`*, *`duration`*, and *`distance`* XML element. When the top-level status code is other than OK, there may be an additional *`error_message`* field within the Distance Matrix response object. This field contains more detailed information about the reasons behind the given status code. For more details on the XML elements and their values, see the Google Maps Distance Matrix API [Developer's Guide.](https://developers.google.com/maps/documentation/distance-matrix/intro) The *`DistanceMatrixResponse`* XML root element is declared in our data binding interface file [`GoogleDistanceMatrix.hpp`](GoogleDistanceMatrix.hpp) as: class DistanceMatrixResponse { DistanceMatrixStatus status; std::string error_USCOREmessage 0; // optional error_message std::vector<std::string> origin_USCOREaddress; std::vector<std::string> destination_USCOREaddress; std::vector<DistanceMatrixRow> row; }; The gSOAP naming conventions for XML data bindings translate each underscore in XML to `_USCORE`, so we use `_USCORE` in place of an underscore in C++ names. The *`error_message`* XML element (`error_USCOREmessage` member) is optional. This can be made a pointer-based member to make it optional (the pointer is NULL when the element is omitted), but in this case we just declare it optional with a `0` minOccurs constraint. We define the *`status`* element for our `DistanceMatrixResponse` class as an enumeration, corresponding to the Distance Matrix API's status codes: typedef enum DistanceMatrixStatus { DistanceMatrixStatus__OK, DistanceMatrixStatus__INVALID_USCOREREQUEST, DistanceMatrixStatus__MAX_USCOREELEMENTS_USCOREEXCEEDED, DistanceMatrixStatus__OVER_USCOREQUERY_USCORELIMIT, DistanceMatrixStatus__REQUEST_USCOREDENIED, DistanceMatrixStatus__UNKNOWN_USCOREERROR, } DistanceMatrixStatus; Again, we used `_USCORE` to translate underscores to `_USCORE`. Note that we prefix the enumeration constants with the enumeration type name. This ensures that the enumeration constants are unique, while still being serialized in their short form. Much nicer is to use C++11 scoped enumerations that do not require prefixing of the enumeration constants. But we will stick to C++ here. Alternatively, we could have just used a string for the type of `status`, but enumerations are strongly typed, meaning the C++ code we will write later to check the API's status code is statically verified to be correct by the compiler. [![To top](../../images/go-up.png) To top](#) ### Rows When the Google Maps Distance Matrix API returns results, it places them within an array. XML responses consist of zero or more *`row`* XML elements. Rows are ordered according to the values in the origin parameter of the request. Each row corresponds to an origin, and each *`element`* within that row corresponds to a pairing of the origin with a destination value. Each row array contains one or more *`element`* entries, which in turn contain the information about a single origin-destination pairing. The container of rows `std::vector<DistanceMatrixRow>` type of the `DistanceMatrixResponse::row` member uses the `DistanceMatrixRow` class, which has a container of elements: class DistanceMatrixRow { std::vector<DistanceMatrixElement> element; }; [![To top](../../images/go-up.png) To top](#) ### Elements The information about each origin-destination pairing is returned in an *`element`* XML entry. The container of elements `std::vector<DistanceMatrixElement>` type of the `DistanceMatrixRow::element` member uses the `DistanceMatrixElement` class: class DistanceMatrixElement { DistanceMatrixEltStatus status; DistanceMatrixDuration duration; DistanceMatrixDuration *duration_USCOREin_USCOREtraffic; // optional duration_in_traffic DistanceMatrixDistance distance; DistanceMatrixFare *fare; // optional fare }; We define the `DistanceMatrixElement::status` as an enumeration, corresponding to the API's status codes for elements: typedef enum DistanceMatrixEltStatus { DistanceMatrixEltStatus__OK, DistanceMatrixEltStatus__NOT_USCOREFOUND, DistanceMatrixEltStatus__ZERO_USCORERESULTS, DistanceMatrixEltStatus__MAX_USCOREROUTE_USCORELENGTH_USCOREEXCEEDED, } DistanceMatrixEltStatus; Again, we could have just used a string for the type of `status`. But enumerations are preferred as strongly typed. However, if a return value is received that is not listed as an enumeration constant then XML parsing and validation will fail with a validation error. This is not a problem with the Google Maps API status codes that are well defined. An *`element`* contains *`duration`* and *`distance`* XML child elements, which are stored in the `DistanceMatrixElement::duration` and `DistanceMatrixElement::distance` members. The types for these members are declared as: class DistanceMatrixDuration { double value; std::string text; }; class DistanceMatrixDistance { double value; std::string text; }; A *`fare`* XML child element may be optionally included with an *`element`*, which we define as: typedef std::string DistanceMatrixCurrency 3:3; class DistanceMatrixFare { DistanceMatrixCurrency currency; double value; std::string text; }; A currency designation is a three-character code as per ISO 4217. Therefore, the min and max length of `DistanceMatrixCurrency` is set to 3. [![To top](../../images/go-up.png) To top](#) ### The XML data binding interface file We put this all together in a file [`GoogleDistanceMatrix.hpp`](GoogleDistanceMatrix.hpp) to declare our XML data binding interface: //////////////////////////////////////////////////////////////////////////////// // // Elements // //////////////////////////////////////////////////////////////////////////////// typedef std::string DistanceMatrixCurrency 3:3; class DistanceMatrixDuration { double value; std::string text; }; class DistanceMatrixDistance { double value; std::string text; }; class DistanceMatrixFare { DistanceMatrixCurrency currency; double value; std::string text; }; typedef enum DistanceMatrixEltStatus { DistanceMatrixEltStatus__OK, DistanceMatrixEltStatus__NOT_USCOREFOUND, DistanceMatrixEltStatus__ZERO_USCORERESULTS, DistanceMatrixEltStatus__MAX_USCOREROUTE_USCORELENGTH_USCOREEXCEEDED, } DistanceMatrixEltStatus; class DistanceMatrixElement { DistanceMatrixEltStatus status; DistanceMatrixDuration duration; DistanceMatrixDuration *duration_USCOREin_USCOREtraffic; // optional duration_in_traffic DistanceMatrixDistance distance; DistanceMatrixFare *fare; // optional fare }; //////////////////////////////////////////////////////////////////////////////// // // Rows // //////////////////////////////////////////////////////////////////////////////// class DistanceMatrixRow { std::vector<DistanceMatrixElement> element; }; //////////////////////////////////////////////////////////////////////////////// // // DistanceMatrixResponse // //////////////////////////////////////////////////////////////////////////////// typedef enum DistanceMatrixStatus { DistanceMatrixStatus__OK, DistanceMatrixStatus__INVALID_USCOREREQUEST, DistanceMatrixStatus__MAX_USCOREELEMENTS_USCOREEXCEEDED, DistanceMatrixStatus__OVER_USCOREQUERY_USCORELIMIT, DistanceMatrixStatus__REQUEST_USCOREDENIED, DistanceMatrixStatus__UNKNOWN_USCOREERROR, } DistanceMatrixStatus; class DistanceMatrixResponse { DistanceMatrixStatus status; std::string error_USCOREmessage 0; // optional error_message std::vector<std::string> origin_USCOREaddress; std::vector<std::string> destination_USCOREaddress; std::vector<DistanceMatrixRow> row; }; [![To top](../../images/go-up.png) To top](#) XML Google Maps Distance Matrix API client source code {#gdmcode} --- The main program constructs and destructs a context `ctx` as follows: // new context with option to parse UTF-8 into strings struct soap *ctx = soap_new1(SOAP_C_UTFSTRING); ... // delete deserialized response and free the context soap_destroy(ctx); soap_end(ctx); soap_free(ctx); For the complete source code, please see [`gdm.cpp`](gdm.cpp). [![To top](../../images/go-up.png) To top](#) ### Constructing the GET URL request Constructing the URL for the GET request is done by collecting the command-line arguments as follows: // construct URL with query string parameters string key = argv[1]; string mode = argv[2]; string units = argv[3]; string origins = argv[4]; string destinations = argv[5]; string URL = service; URL += "?key=" + urlencode(key); URL += "&mode=" + urlencode(mode); URL += "&units=" + urlencode(units); URL += "&origins=" + urlencode(origins); URL += "&destinations=" + urlencode(destinations); // Google API permits up to 8K URL lengths if (URL.size() > 8192) { printf("URL too long\n\n"); exit(EXIT_FAILURE); } where function `urlencode` is defined as: static string urlencode(const string& plain) { string encoded; for (size_t i = 0; i < plain.size(); ++i) { int c = plain.at(i); if (c == ' ') { encoded.push_back('+'); } else if (c == '!' || c == '$' || (c >= '(' && c <= '.') || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= 'a' && c <= 'z')) { encoded.push_back(c); } else { encoded.push_back('%'); encoded.push_back((c >> 4) + (c > 159 ? '7' : '0')); c &= 0xF; encoded.push_back(c + (c > 9 ? '7' : '0')); } } return encoded; } This creates a `URL` string that we will use to invoke the Google Maps API. [![To top](../../images/go-up.png) To top](#) ### Connecting to the Google Maps API The source code establishes a HTTPS connection. The server is not verified unless you enable: if (soap_ssl_client_context(ctx, SOAP_SSL_DEFAULT, NULL, NULL, "cacerts.pem", NULL, NULL)) { soap_stream_fault(ctx, cerr); exit(EXIT_FAILURE); } When running the command-line application, a `cacerts.pem` file in the current directory is required. Or compile `plugin/cacerts.c` with hard-coded CA certificates (and change the source code as needed). We implement exponential backoff when HTTP 5xx errors occur, as recommended by Google in the [Best Practices Using Google Maps APIs](https://developers.google.com/maps/documentation/distance-matrix/web-service-best-practices#ParsingXML). // construct XML response object DistanceMatrixResponse response; // exponential backoff float delay = 0.100; // initial 100 ms delay float delay_factor = 2; // delay doubles for each retry float max_delay = 120; // 2 minutes max delay // GET XML response - exponential backoff on HTTP 5xx errors while (soap_GET_DistanceMatrixResponse(ctx, URL.c_str(), &response)) { if (delay <= max_delay && ctx->error >= 500 && ctx->error < 599) { usleep((useconds_t)(1000 * delay)); delay *= delay_factor; } else { soap_stream_fault(ctx, cerr); break; } } After the connection is made, the `response` structure contains the Google Maps API response parsed from XML. All the hard XML parsing and deserialization work is done by the `DistanceMatrixResponse` class and the auto-generated `soap_GET_DistanceMatrixResponse` function for this class. [![To top](../../images/go-up.png) To top](#) ### Printing the response To print the response we check for success and then iterate over the rows: // success? if (ctx-error == SOAP_OK) { if (response.status == DistanceMatrixStatus__OK) { cout << "----------------------------------------" << endl; // for each origin and destination pair, print duration and distance size_t n = minsize(response.origin_USCOREaddress.size(), response.row.size()); for (size_t i = 0; i < n; ++i) { cout << "from: " << response.origin_USCOREaddress[i] << endl; size_t m = minsize(response.destination_USCOREaddress.size(), response.row[i].element.size()); for (size_t j = 0; j < m; ++j) { cout << "to: " << response.destination_USCOREaddress[j] << endl; const DistanceMatrixElement& e = response.row[i].element[j]; if (e.status == DistanceMatrixEltStatus__OK) { cout << "duration: " << e.duration.value << " (" << e.duration.text << ")" << endl; if (e.duration_USCOREin_USCOREtraffic) cout << "duration in traffic: " << e.duration_USCOREin_USCOREtraffic->value << " (" << e.duration_USCOREin_USCOREtraffic->text << ")" << endl; cout << "distance: " << e.distance.value << " (" << e.distance.text << ")" << endl; if (e.fare) cout << "fare: " << e.fare->currency << " " << e.fare->value << " (" << e.fare->text << ")" << endl; } else { cout << "error: " << soap_DistanceMatrixEltStatus2s(ctx, e.status) << endl; } } cout << "----------------------------------------" << endl; } cout << endl; } else { cerr << "error: " << soap_DistanceMatrixStatus2s(ctx, response.status) << " " << response.error_USCOREmessage << endl; if (response.status == DistanceMatrixStatus__UNKNOWN_USCOREERROR) cerr << "UNKNOWN ERROR: try again" << endl; } } where `minsize()` is defined as `inline size_t minsize(size_t a, size_t b) { return a < b ? a : b; }`. [![To top](../../images/go-up.png) To top](#) XML Google Maps Distance Matrix API client application build steps {#gdmbuild} --- Because we use a C++ namespace in the [`gdm.hpp`](gdm.hpp) interface header file, we also need a `env.h` file with global non-C++-namespaced definitions. This is where we normally put the SOAP Header and SOAP Fault details. However, we do not use SOAP so this file is simply empty. To build the Google Maps Distance Matrix `gdm` command-line application: [command] soapcpp2 -0 gdm.hpp soapcpp2 -0 -penv env.h c++ -DWITH_OPENSSL -o gdm gdm.cpp stdsoap2.cpp gdmC.cpp envC.cpp -lssl -lcrypto The soapcpp2 commands generate the following non-SOAP (`-0` option) source code files and a report (`-r` option): * [`gdmStub.h`](gdmStub.h) a copy of the specification in plain C/C++ header file syntax without annotations. * [`gdmH.h`](gdmH.h) declares XML serializers. * [`gdmC.cpp`](gdmC.cpp) implements XML serializers. * [`gdmReadme.md`](gdmReadme.html) service and data binding interface details. * [`envStub.h`](envStub.h) a copy of the specification in plain C/C++ header file syntax without annotations. * [`envH.h`](envH.h) declares XML serializers. * [`envC.cpp`](envC.cpp) implements XML serializers. [![To top](../../images/go-up.png) To top](#) Using the Google Maps Distance Matrix client application {#gdmuse} --- To use the Google Maps Distance Matrix `gdm` command-line application: gdm <YOUR_API_KEY> <mode> <units> 'origin addresses' 'destination addresses' where `YOUR_API_KEY` is the API key you received earlier, mode is either `driving`, `walking`, `bicycling`, or `transit`. The units parameter should be `metric` or `imperial`. Multiple addresses, when provided, should be separated by `|`. The addresses are URL-encoded by `gdm` before being passed to the Google Maps Distance Matrix API. [![To top](../../images/go-up.png) To top](#) XML Google Maps Directions API with gSOAP {#gdxapi} --- The Google Maps Directions API returns the most efficient routes when calculating directions. Travel time is the primary factor optimized, but the API may also take into account other factors such as distance, number of turns and many more when deciding which route is the most efficient. Calculating directions is a time and resource intensive task. Whenever possible, use the service described here to calculate known addresses ahead of time and store the results in a temporary cache of your own design. A [Google Maps Directions API GET request URL](https://developers.google.com/maps/documentation/directions/intro#RequestParameters) takes the following form: [command] https://maps.googleapis.com/maps/api/directions/outputFormat?parameters where `outputFormat` may be either `json` or `xml`. To use the Google Maps Directions API you need an API key. [Get an API key.](https://developers.google.com/maps/documentation/directions/get-api-key) Review the [usage limits](https://developers.google.com/maps/documentation/directions/usage-limits) page for details on the quotas set for the Google Maps Distance Matrix API. Use of the Google Maps Distance Matrix API must be in accordance with the [API policies.](https://developers.google.com/maps/documentation/directions/policies) The `gdx` example client applications uses the Google Maps APIs with XML responses. To use XML data bindings with gSOAP to parse this XML directly into C++ objects, we defined an interface header file using a similar approach as the Google Maps Distance Matrix API. Because this file is larger, we do not list its contents here. For the full listing, see [`GoogleDirections.hpp`](GoogleDirections.hpp). [![To top](../../images/go-up.png) To top](#) XML Google Maps Directions API client source code {#gdxcode} --- The [`gdx.cpp`](gdx.cpp) source code has a similar structure as [`gdm.cpp`](gdm.cpp). For the complete source code, please see [`gdx.cpp`](gdx.cpp). [![To top](../../images/go-up.png) To top](#) ### Constructing the GET URL request // construct URL with query string parameters string key = argv[1]; string mode = argv[2]; string units = argv[3]; string origin = argv[4]; string destination = argv[5]; string URL = service; URL += "?key=" + urlencode(key); URL += "&mode=" + urlencode(mode); URL += "&units=" + urlencode(units); URL += "&origin=" + urlencode(origin); URL += "&destination=" + urlencode(destination); // Google API permits up to 8K URL lengths if (URL.size() > 8192) { printf("URL too long\n\n"); exit(EXIT_FAILURE); } [![To top](../../images/go-up.png) To top](#) ### Connecting to the Google Maps API // construct XML response object DirectionsResponse response; // exponential backoff float delay = 0.100; // initial 100 ms delay float delay_factor = 2; // delay doubles for each retry float max_delay = 120; // 2 minutes max delay // GET XML response - exponential backoff on HTTP 5xx errors while (soap_GET_DirectionsResponse(ctx, URL.c_str(), &response)) { if (delay <= max_delay && ctx->error >= 500 && ctx->error < 599) { usleep((useconds_t)(1000 * delay)); delay *= delay_factor; } else { soap_stream_fault(ctx, cerr); break; } } [![To top](../../images/go-up.png) To top](#) ### Printing the response // success? if (ctx->error == SOAP_OK) { if (response.status == DirectionsStatus__OK) { for (std::vector<std::string>::const_iterator i = response.available_USCOREtravel_USCOREmodes.begin(); i != response.available_USCOREtravel_USCOREmodes.end(); ++i) cout << "available travel mode: " << *i << endl; for (std::vector<DirectionsRoute>::const_iterator i = response.route.begin(); i != response.route.end(); ++i) { cout << "----------------------------------------" << endl; cout << "summary: " << i->summary << endl; for (std::vector<std::string>::const_iterator j = i->warning.begin(); j != i->warning.end(); ++j) cout << "warning: " << *j << endl; if (i->fare) cout << "fare: " << i->fare->currency << " " << i->fare->value << " (" << i->fare->text << ")" << endl; cout << "copyrights: " << i->copyrights << endl << endl; for (std::vector<DirectionsLeg>::const_iterator j = i->leg.begin(); j != i->leg.end(); ++j) { cout << "from " << j->start_USCOREaddress << " to " << j->end_USCOREaddress << " takes"; if (j->distance) cout << " " << j->distance->value << " meters (" << j->distance->text << ")"; if (j->duration) cout << " " << j->duration->value << " seconds (" << j->duration->text << ")"; if (j->duration_USCOREin_USCOREtraffic) cout << " " << j->duration_USCOREin_USCOREtraffic->value << " seconds in traffic (" << j->duration_USCOREin_USCOREtraffic->text << ")"; cout << endl << endl; for (std::vector<DirectionsStep>::const_iterator k = j->step.begin(); k != j->step.end(); ++k) { if (!k->html_USCOREinstructions.empty()) cout << k->html_USCOREinstructions << endl; if (k->maneuver) cout << *k->maneuver << ", "; cout << k->travel_USCOREmode; if (k->distance) cout << " " << k->distance->value << " meters (" << k->distance->text << ")"; if (k->duration) cout << " " << k->duration->value << " seconds (" << k->duration->text << ")"; cout << endl; for (std::vector<DirectionsStep>::const_iterator l = k->step.begin(); l != k->step.end(); ++l) { if (!l->html_USCOREinstructions.empty()) cout << " " << l->html_USCOREinstructions << endl; cout << " => "; if (l->maneuver) cout << *l->maneuver << ", "; cout << l->travel_USCOREmode; if (l->distance) cout << " " << l->distance->value << " meters (" << l->distance->text << ")"; if (l->duration) cout << " " << l->duration->value << " seconds (" << l->duration->text << ")"; cout << endl; } if (k->transit_USCOREdetails) { DirectionsTransitDetails& d = *k->transit_USCOREdetails; cout << " take transit at " << d.departure_USCOREtime.text << " from " << d.departure_USCOREstop.name << " to " << d.arrival_USCOREstop.name << " arriving at " << d.arrival_USCOREtime.text << ":" << endl; cout << " => " << d.line.vehicle.name << " " << d.line.name << " line " << d.line.short_USCOREname << " heading " << d.headsign << " and exit at stop " << d.num_USCOREstops << endl; for (std::vector<DirectionsAgency>::const_iterator l = d.line.agency.begin(); l != d.line.agency.end(); ++l) cout << " (operated by " << l->name << " " << l->url << ") " << endl; } cout << endl; } } } cout << endl; } else { cerr << "error: " << soap_DirectionsStatus2s(ctx, response.status) << " " << response.error_USCOREmessage << endl; if (response.status == DirectionsStatus__UNKNOWN_USCOREERROR) cerr << "UNKNOWN ERROR: try again" << endl; } } [![To top](../../images/go-up.png) To top](#) XML Google Maps Directions API client application build steps {#gdxbuild} --- Because we use a C++ namespace in the `gdm.hpp` interface header file, we also need a `env.h` file with global non-C++-namespaced definitions. This is where we normally put the SOAP Header and SOAP Fault details. However, we do not use SOAP so this file is simply empty. To build the Google Maps Directions `gdx` command-line application: [command] soapcpp2 -0 gdm.hpp soapcpp2 -0 -penv env.h c++ -DWITH_OPENSSL -o gdx gdx.cpp stdsoap2.cpp gdmC.cpp envC.cpp -lssl -lcrypto The soapcpp2 commands generate the following non-SOAP (`-0` option) source code files and a report (`-r` option): * [`gdmStub.h`](gdmStub.h) a copy of the specification in plain C/C++ header file syntax without annotations. * [`gdmH.h`](gdmH.h) declares XML serializers. * [`gdmC.cpp`](gdmC.cpp) implements XML serializers. * [`gdmReadme.md`](gdmReadme.html) service and data binding interface details. * [`envStub.h`](envStub.h) a copy of the specification in plain C/C++ header file syntax without annotations. * [`envH.h`](envH.h) declares XML serializers. * [`envC.cpp`](envC.cpp) implements XML serializers. [![To top](../../images/go-up.png) To top](#) Using the Google Maps Directions client application {#gdxuse} --- To use the Google Maps Directions `gdx` command-line application: gdx <YOUR_API_KEY> <mode> <units> 'origin address' 'destination address' where `YOUR_API_KEY` is the API key you received earlier, mode is either `driving`, `walking`, `bicycling`, or `transit`. The units parameter should be `metric` or `imperial`. An origina address and a destination address should be provided. The addresses are URL-encoded by `gdx` before being passed to the Google Maps Directions API. [![To top](../../images/go-up.png) To top](#) JSON Google Maps Distance Matrix API with gSOAP {#json-gdmapi} --- The Google Maps Distance Matrix API returns information based on the recommended route between start and end points, as calculated by the Google Maps API, and consists of rows containing duration and distance values for each pair. A [Google Maps Distance Matrix API GET request URL](https://developers.google.com/maps/documentation/distance-matrix/intro#DistanceMatrixRequests) takes the following form: [command] https://maps.googleapis.com/maps/api/distancematrix/outputFormat?parameters where `outputFormat` may be either `json` or `xml`. To use the Google Maps Distance Matrix API you need an API key. [Get an API key.](https://developers.google.com/maps/documentation/distance-matrix/get-api-key) Review the [usage limits](https://developers.google.com/maps/documentation/distance-matrix/usage-limits) page for details on the quotas set for the Google Maps Distance Matrix API. Use of the Google Maps Distance Matrix API must be in accordance with the [API policies.](https://developers.google.com/maps/documentation/distance-matrix/policies) The `json-gdm` example client applications uses the Google Maps APIs with JSON responses. For example, given "Vancouver BC" and "Seattle" as origins, "San Fransicso" and "Vancouver BS" as destinations, the following GET request computes the bycycling durations and distance for the four origin-destination pair combinations: [command] https://maps.googleapis.com/maps/api/distancematrix/json?origins=Vancouver+BC|Seattle&destinations=San+Francisco|Vancouver+BC&mode=bicycling&units=imperial&key=YOUR_API_KEY This returns the following JSON response by the API: [json] { "status": "OK", "origin_addresses": [ "Vancouver, BC, Canada", "Seattle, État de Washington, États-Unis" ], "destination_addresses": [ "San Francisco, Californie, États-Unis", "Victoria, BC, Canada" ], "rows": [ { "elements": [ { "status": "OK", "duration": { "value": 340110, "text": "3 jours 22 heures" }, "distance": { "value": 1734542, "text": "1 735 km" } }, { "status": "OK", "duration": { "value": 24487, "text": "6 heures 48 minutes" }, "distance": { "value": 129324, "text": "129 km" } } ] }, { "elements": [ { "status": "OK", "duration": { "value": 288834, "text": "3 jours 8 heures" }, "distance": { "value": 1489604, "text": "1 490 km" } }, { "status": "OK", "duration": { "value": 14388, "text": "4 heures 0 minutes" }, "distance": { "value": 135822, "text": "136 km" } } ] } ] } To use JSON we use the [JSON/JSONPath library](https://www.genivia.com/doc/xml-rpc-json/html/index.html) of gSOAP. [![To top](../../images/go-up.png) To top](#) JSON Google Maps Distance Matrix API source code {#json-gdmcode} --- To use JSON we include [`json.h`](json.h) from the gsoap/samples/xml-rpc-json directory of the gSOAP source code tree. #include "json.h" The main program constructs and destructs a context `ctx` as follows: // new context with option to parse UTF-8 into strings struct soap *ctx = soap_new1(SOAP_C_UTFSTRING); ... // delete deserialized response and free the context soap_destroy(ctx); soap_end(ctx); soap_free(ctx); For the complete source code, please see [`json-GoogleDistanceMatrix.cpp`](json-GoogleDistanceMatrix.cpp). [![To top](../../images/go-up.png) To top](#) ### Constructing the GET URL request Constructing the URL for the GET request is done by collecting the command-line arguments as follows: // construct URL with query string parameters string key = argv[1]; string mode = argv[2]; string units = argv[3]; string origins = argv[4]; string destinations = argv[5]; string URL = service; URL += "?key=" + urlencode(key); URL += "&mode=" + urlencode(mode); URL += "&units=" + urlencode(units); URL += "&origins=" + urlencode(origins); URL += "&destinations=" + urlencode(destinations); // Google API permits up to 8K URL lengths if (URL.size() > 8192) { printf("URL too long\n\n"); exit(EXIT_FAILURE); } where function `urlencode` is defined as: static string urlencode(const string& plain) { string encoded; for (size_t i = 0; i < plain.size(); ++i) { int c = plain.at(i); if (c == ' ') { encoded.push_back('+'); } else if (c == '!' || c == '$' || (c >= '(' && c <= '.') || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= 'a' && c <= 'z')) { encoded.push_back(c); } else { encoded.push_back('%'); encoded.push_back((c >> 4) + (c > 159 ? '7' : '0')); c &= 0xF; encoded.push_back(c + (c > 9 ? '7' : '0')); } } return encoded; } This creates a `URL` string that we will use to invoke the Google Maps API. [![To top](../../images/go-up.png) To top](#) ### Connecting to the Google Maps API The source code establishes a HTTPS connection. The server is not verified unless you enable: if (soap_ssl_client_context(ctx, SOAP_SSL_DEFAULT, NULL, NULL, "cacerts.pem", NULL, NULL)) { soap_stream_fault(ctx, cerr); exit(EXIT_FAILURE); } When running the command-line application, a `cacerts.pem` file in the current directory is required. Or compile `plugin/cacerts.c` with hard-coded CA certificates (and change the source code as needed). We implement exponential backoff when HTTP 5xx errors occur, as recommended by Google in the [Best Practices Using Google Maps APIs](https://developers.google.com/maps/documentation/distance-matrix/web-service-best-practices#ParsingXML). // construct JSON response object value response(ctx); // exponential backoff float delay = 0.100; // initial 100 ms delay float delay_factor = 2; // delay doubles for each retry float max_delay = 120; // 2 minutes max delay // GET JSON response - exponential backoff on HTTP 5xx errors while (json_call(ctx, URL.c_str(), NULL, response)) { if (delay <= max_delay && ctx->error >= 500 && ctx->error < 599) { usleep((useconds_t)(1000 * delay)); delay *= delay_factor; } else { soap_stream_fault(ctx, cerr); break; } } A `json_call` takes a context, an endpoint URL (with URL query string parameters as needed), and optional `in` and `out` values to send and receive, respectively. This function returns `SOAP_OK` (zero) for success or `EOF`, `SOAP_SYNTAX_ERROR`, or an HTTP error code. In this case the `json_call` function sends a HTTP GET request. The NULL parameter after the URL tells `json_call` to use GET, because there is no JSON data to send. To use the HTTP POST method with `json_call`, simply pass both `in` and `out` values to `json_call`. For the GET method, pass a NULL to `in`. For the PUT method, pass a NULL to `out`. For the DELETE method, pass both NULL to `in` and `out`. After the connection is made, the `response` JSON value contains the Google Maps API response parsed from JSON. [![To top](../../images/go-up.png) To top](#) ### Printing the response To print the response we check for success and then iterate over the rows: // success? if (ctx->error == SOAP_OK) { if (response.has("status") && response.has("origin_addresses") && response.has("destination_addresses") && response.has("rows")) { if (!strcmp(response["status"], "OK")) { cout << "----------------------------------------" << endl; // for each origin and destination pair, print duration and distance size_t n = minsize(response["origin_addresses"].size(), response["rows"].size()); for (size_t i = 0; i < n; ++i) { cout << "from: " << response["origin_addresses"][i] << endl; if (response["rows"][i].has("elements")) { size_t m = minsize(response["destination_addresses"].size(), response["rows"][i]["elements"].size()); for (size_t j = 0; j < m; ++j) { cout << "to: " << response["destination_addresses"][j] << endl; value& e = response["rows"][i]["elements"][j]; if (e.has("status") && !strcmp(e["status"], "OK")) { if (e.has("duration") && e["duration"].has("value") && e["duration"].has("text")) cout << "duration: " << e["duration"]["value"] << " (" << e["duration"]["text"] << ")" << endl; if (e.has("duration_in_traffic") && e["duration_in_traffic"].has("value") && e["duration_in_traffic"].has("text")) cout << "duration in traffic: " << e["duration_in_traffic"]["value"] << " (" << e["duration_in_traffic"]["text"] << ")" << endl; if (e.has("distance") && e["distance"].has("value") && e["distance"].has("text")) cout << "distance: " << e["distance"]["value"] << " (" << e["distance"]["text"] << ")" << endl; if (e.has("fare") && e["fare"].has("currency") && e["fare"].has("value") && e["fare"].has("text")) cout << "fare: " << e["fare"]["currency"] << e["fare"]["value"] << " (" << e["fare"]["text"] << ")" << endl; } else { cout << "error: "; if (e.has("status")) cout << e["status"]; cout << endl; } cout << "----------------------------------------" << endl; } } } } else { cerr << "error: "; if (response.has("status")) cerr << response["status"]; if (response.has("error_message")) cerr << " " << response["error_message"]; cerr << endl; } } } else { soap_stream_fault(ctx, cerr); } where `minsize()` is defined as `inline size_t minsize(size_t a, size_t b) { return a < b ? a : b; }`. The source code above uses the [JSON/JSONPath library](https://www.genivia.com/doc/xml-rpc-json/html/index.html) of gSOAP. As you can see, JSON values can be send to output streams (and parsed from input streams). To retrieve JSON values in C++, just cast the `value` to a target type that can be a bool, an integer, a (double) float, or a string. To inspect and retrieve object properties, use: - `v.empty()` returns true if object `v` is empty. - `v.has("name")` returns true if `v` has property `name`. - `v["name"]` returns value of property `name` of object `v`, creates a new `name` property with value `null` if the property does not already exist. None of these methods raise exceptions. To retrieve array elements, use: - `v.empty()` returns true if array `v` is empty. - `v.size()` returns array size of `v`. - `v.nth(index)` returns `index` if `index` is within array bounds or returns a negative integer otherwise. - `v[index]` returns value of array element at `index`, creates array and/or extends size of array to `index + 1` (thereby setting the extra values to null) if the array is too small. None of these methods raise exceptions. The following methods are used to type-check a JSON value: - `v.is_null()` returns true if `v` is null. - `v.is_bool()` returns true if `v` is Boolean. - `v.is_true()` returns true if `v` is Boolean true. - `v.is_false()` returns true if `v` is Boolean false. - `v.is_number()` returns true if `v` is a number (integer or float). - `v.is_int()` returns true if `v` is an integer. - `v.is_double()` returns true if `v` is a double precision float. - `v.is_string()` returns true if `v` is a string. - `v.is_array()` returns true if `v` is an array. - `v.is_struct()` returns true if `v` is an object (structure). See the [JSON/JSONPath library](https://www.genivia.com/doc/xml-rpc-json/html/index.html) of gSOAP for more details. This library also has a C version with C functions for JSON objects, arrays, bool, integer, float, and string. [![To top](../../images/go-up.png) To top](#) JSON Google Maps Distance Matrix API client application build steps {#json-gdmbuild} --- To build the Google Maps Distance Matrix `json-gdm` command-line application: [command] soapcpp2 -CSL xml-rpc.h c++ -DWITH_OPENSSL -o json-gdm json-GoogleDistanceMatrix.cpp xml-rpc.cpp json.cpp soapC.cpp stdsoap2.cpp -lssl -lcrypto The soapcpp2 commands generate the following non-SOAP (`-0` option) source code files and a report (`-r` option): * [`soapStub.h`](soapStub.h) a copy of the specification in plain C/C++ header file syntax without annotations. * [`soapH.h`](soapH.h) declares XML serializers. * [`soapC.cpp`](soapC.cpp) implements XML serializers. The following files are part of the gsoap/samples/xml-rpc-json directory of the gSOAP source code tree: * [`xml-rpc.h`](xml-rpc.h) interface header file with XML-RPC (and JSON) structures * [`xml-rpc.cpp`](xml-rpc.cpp) XML-RPC implementation (required for JSON too) * [`json.h`](json.h) JSON C/C++ library API * [`json.cpp`](json.cpp) JSON C/C++ library API [![To top](../../images/go-up.png) To top](#) For C developers --- A [`GoogleDistanceMatrix.h`](GoogleDistanceMatrix.h) interface file for C is included to build clients in C. However, no C application example is provided in source code. Use the C++ code as a guideline. [![To top](../../images/go-up.png) To top](#) Readme report ------------- See the auto-generated [gdmReadme](gdmReadme.html) for this example. [![To top](../../images/go-up.png) To top](#)