Thursday, July 7, 2011

GeoCoding with C++ and Google Geocoding API V3 (using libCURL and JsonCpp)

Geocoding is the process of finding associated geographic coordinates (often expressed as latitude and longitude) from other geographic data, such as street addresses, or zip codes (postal codes). (read more in wikipedia)

Today I'll give a short example of C++ application in which we will use the newest version of Google Geocoding API V3. You can compare it with similar C# geocoding example.

When you perform a geocoder request, you can specify which output type to use. The available options are JSON, XML, KML and CSV. We will be requesting and handling JSON data.

For example, if you wanted to find the coordinates of house located at 1600 Amphitheatre Parkway, Mountain View, CA, you would request the following URL: (http://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&sensor=false).

We will use cURL libcurl library for requests.

The response will be processed by using JsonCpp library and coordinates will be printed to console.

OK, here is the code of my geocoder example:
#include <string>
#include <iostream>
#include "json/json.h"
#include "CURLWrapper.h"

static std::string buffer;

static int writer(char *data, size_t size, size_t nmemb, std::string *buffer)
{
    int result = 0;
    if (buffer != NULL)
    {
        buffer->append(data, size * nmemb);
        result = size * nmemb;
    }

    return result;
}

int main()
{
    std::string url = "http://maps.googleapis.com/maps/api/geocode/json?"
   "address=1600+Amphitheatre+Parkway,+Mountain+View,"
   "+CA&sensor=false";

    std::cout << "Will retrive from url: '" << url << "'" << std::endl;

    CURLWrapper::Easy easy;
    easy.SetOption(CURLOPT_URL, url.c_str());
    easy.SetOption(CURLOPT_HEADER, 0);
    easy.SetOption(CURLOPT_FOLLOWLOCATION, 1);
    easy.SetOption(CURLOPT_WRITEFUNCTION, writer);
    easy.SetOption(CURLOPT_WRITEDATA, &buffer);
    easy.Perform();

    if (easy.IsOK())
    {
        std::cout << "CURL: response is OK " << std::endl;
        //std::cout << buffer << std::endl;
 
        if (!buffer.empty())
        {
            Json::Value root;
            Json::Reader reader;
     
            if (reader.parse(buffer, root))
            {
                std::cout << "JSON response is OK!" << std::endl;
  
                const Json::Value status = root["status"];
                std::cout << "JSON response status: '" 
                    << status.asString() << "'" 
                    << std::endl;

                if (status.asString() == "OK")
                {
                    const Json::Value results = root["results"];
      
                    std::cout << "JSON response results.size = " 
                        << results.size() << std::endl;

                    for(unsigned int i = 0; i < results.size(); i++)
                    {
                        const Json::Value geometry = results[i]["geometry"];
   
                        std::cout << "Coordinates: " 
                            << "lat=" << geometry["location"]["lat"].asString()
                            << " "
                            << "lng=" << geometry["location"]["lng"].asString()
                            << std::endl;
                    }
                }
                else
                {
                    std::cout << "JSON response contains no results" 
                        << std::endl;
                }
            }
            else
            {
                std::cout << "JSON response Error: "  
                    << reader.getFormattedErrorMessages() 
                    << std::endl;
            }
        }
        else
        {
            std::cout << "CURL: respose is empty! " << std::endl;
        }
    }
    else
    {
        std::cout << "CURL Error: [ " << easy.GetError() << " ] - " 
            << easy.GetErrorMessage() << std::endl;
    }
}


In order to compile this example you would also need my libcurl wrapper class: CURLWrapper (which is heavily inspired by cURLpp project). Here is the code for it:
"CURLWrapper.h" file
#ifndef CURL_WRAPPER_H
#define CURL_WRAPPER_H

#include <string>
#include "curl/curl.h"

namespace CURLWrapper 
{
    class Easy
    {
    public:
        Easy();
        ~Easy();

        void Init();
        void Cleanup();
        void Reset();

        template <typename T, typename V>
        void SetOption(T option, V parameter)
        {
            curl_easy_setopt(mCurl, option, parameter);
        }

        void Perform();
        bool IsOK();
        CURLcode GetError();
        std::string GetErrorMessage();
    
    private:
        void ErrorBuffer(char* buffer); 
    
    private:
        CURL* mCurl;
        char mErrorBuffer[CURL_ERROR_SIZE + 1];
        CURLcode mCode;
    };

}

#endif //#ifndef CURL_WRAPPER_H


"CURLWrapper.cpp" file
#include <memory>
#include <algorithm>
#include "CURLWrapper.h"
#include "curl/curl.h"

CURLWrapper::Easy::Easy()
    : mCurl(curl_easy_init()),
    mCode(CURLE_OK)
{
}

CURLWrapper::Easy::~Easy()
{
    curl_easy_cleanup(mCurl);
}

void CURLWrapper::Easy::Init()
{
}

void CURLWrapper::Easy::Cleanup()
{
    curl_easy_cleanup(mCurl);
}

void CURLWrapper::Easy::Reset()
{
    curl_easy_reset(mCurl);
    std::fill(mErrorBuffer, mErrorBuffer + CURL_ERROR_SIZE + 1, 0);
    ErrorBuffer(mErrorBuffer);
    mCode = CURLE_OK;
}

void CURLWrapper::Easy::ErrorBuffer(char* buffer)
{
    SetOption(CURLOPT_ERRORBUFFER, (void*)buffer);
}

void CURLWrapper::Easy::Perform()
{
    mCode = curl_easy_perform(mCurl);
}

bool CURLWrapper::Easy::IsOK()
{
    return mCode == CURLE_OK;
}

CURLcode CURLWrapper::Easy::GetError()
{
    return mCode;
}

std::string CURLWrapper::Easy::GetErrorMessage()
{
    return std::string(mErrorBuffer);
}


You can compile example like this:
g++ -g -Wall -Ilibs/include -Llibs/lib -o prog prog.cpp CURLWrapper.cpp -lcurl -ljson_linux-gcc-4.5.2_libmt

And here is result screenshot: