Wednesday 10 February 2021

Integrate Kakao API with Sitecore

Hello Sitecore Developers,

We recently came across a requirement where client wanted to use Kakao API to get Latitude and Longitude for South Korean Addresses in Sitecore.

Kakao is an API service specifically used in South Korea which provide similar kind of services as Google or Bing maps.

To Learn more about Kakao:

https://developers.kakao.com/

We need to create an App to use the functionalities of Kakao. Since we are using Sitecore we would be using Web platform in Kakao app which provides Javascript/Rest API support. Once the App is created it would provide with an API key which we can use in our code to use Kakao services.

Below article provides information on how to create an app in Kakao

https://developers.kakao.com/docs/latest/ko/getting-started/app                                          

We were having fields like Address, City, State, Postal Code etc. typically used to get the Latitude and Longitude values.

But the challenge with Kakao API is the Language of the address to pass, it only allows Korean address for conversion.

Content Items in our system were having Shared fields and had addresses in English Language only.

We decided to use google translate api to send the English addresses and convert them to Korean and pass it through Kakao to get the Lat and Long values.

Here is the link to setup Google Translate API.

https://cloud.google.com/translate/docs

 

Code:

We have integrated Kakao Api with Data Exchange Framework where our custom pipeline executes and collects data from external system and create items in Sitecore. We pass Address, City, StateOrProvince, Country etc. to

Google Translate -> Translate the Address values to Korean Language - > Sends the Korean address to Kakao -> Sends Lat and Long values in response and update item fields.

 

public async Task<Geolocation> GetLatLongfromAddressViaKakaoAsync(string address, string city, string stateOrProvince, string country)

        {

            Geolocation latlong = null;

            double propertyLatidude = 0.0;

            double propertyLongitude = 0.0;

            var addressQuery = string.Join(" ", new[] { address, city, stateOrProvince, country }.Where(s => !string.IsNullOrEmpty(s)));

            if (string.IsNullOrEmpty(addressQuery))

            {

                Log.Warn($"[{GetType().FullName}.{nameof(GetLatLongfromAddressViaKakaoAsync)}] Could not get geolocation data because no address parameters were populated", this);

            }

            else

            {

                var cleanAddress = Regex.Replace(addressQuery, @"[^0-9a-zA-Z-:, ]+", "");

                string koreanAddress =TranslateAddressToKoreanViaGoogleTranslate(cleanAddress);

 

                if (string.IsNullOrEmpty(koreanAddress))

                {

          Log.Warn($"[{GetType().FullName}.{nameof(GetLatLongfromAddressViaKakaoAsync)}] Could not get Korean Address Via Google Translate API", this);

                }

                else

                {

                    HttpWebRequest request = KakaoRequest(koreanAddress);

 

                    var content = string.Empty;

 

                    using (var response = (HttpWebResponse)request?.GetResponse())

                    {

                        if (response?.StatusCode == HttpStatusCode.OK)

                        {

                            using (var stream = response?.GetResponseStream())

                            {

                                if (stream != null && stream != Stream.Null)

                                {

                                    using (var sr = new StreamReader(stream))

                                    {

                                        content = await sr?.ReadToEndAsync();

                                        var jsonData = JsonConvert.DeserializeObject<KakaoGeoCodeResults>(content);

                                        if (jsonData?.documents != null && jsonData?.documents?.Count > 0)

                                        {

                                            double.TryParse(jsonData?.documents?.FirstOrDefault()?.y, out propertyLatidude);

                                            double.TryParse(jsonData?.documents?.FirstOrDefault()?.x, out propertyLongitude);

                                            latlong = new Geolocation

                                            {

                                                Latitude = propertyLatidude,

                                                Longitude = propertyLongitude

                                            };

                                        }

                                        else

                                        {

                                            Log.Warn($"[{GetType().FullName}.{nameof(GetLatLongfromAddressViaKakaoAsync)}] Could not get geolocation data for address query via Kakao Geocoding service for: English : {addressQuery} - Korea: {koreanAddress}", this);

                                        }

                                        sr?.Dispose();

                                    }

                                }

                                else

                                {

                                    Log.Warn($"[{GetType().FullName}.{nameof(GetLatLongfromAddressViaKakaoAsync)}] Response Stream is null from Kakao Response for: English : {addressQuery} - Korea: {koreanAddress}", this);

                                }

                            }

                        }

                        else

                        {

                            Log.Error($"[{GetType().FullName}.{nameof(GetLatLongfromAddressViaKakaoAsync)}] Error(s) received from Kakao Geocode Provider for: English : {addressQuery} - Korea: {koreanAddress} having Errors : {response?.StatusDescription}", this);

                        }

                        response?.Dispose();

                    }

                }

            }

            return latlong;

        }

private HttpWebRequest KakaoRequest(string koreaAddress)

        {

            var request = (HttpWebRequest)WebRequest.Create("https://dapi.kakao.com//v2/local/search/address.json?query=" + koreaAddress);

            request.Headers["Authorization"] = "KakaoAK " + KakaoApiKey;

            request.Method = "GET";

            request.ContentType = "application/json;charset=UTF-8";

            return request;

        }

        private string TranslateAddressToKoreanViaGoogleTranslate(string address)

        {

            var url = "https://translation.googleapis.com/language/translate/v2?key=" + GoogleApiKey + "&source=en&target=ko&q=" + address;

            using (var client = new WebClient())

            {

                client.Encoding = Encoding.UTF8;

                var translatedData = client?.DownloadString(url);

                if (string.IsNullOrEmpty(translatedData))

                    return null;

                var jsonTranslatedData = JsonConvert.DeserializeObject<GoogleTranslateData>(translatedData);

                return jsonTranslatedData?.Data?.Translations?.FirstOrDefault()?.TranslatedText;

            }

        }

 

Below are the model classes used to get response from Google Translate and kakao Rest API

 

public class GoogleTranslationsList

    {

        public List<GoogleTranslateText> Translations { get; set; }

    }

public class GoogleTranslateText

    {

        public string TranslatedText { get; set; }

    }

public class GoogleTranslateText

    {

        public string TranslatedText { get; set; }

    }

 

public class KakaoDocument

    {

        public string x { get; set; }

        public string y { get; set; }

    }

public class KakaoDocument

    {

        public string x { get; set; }

        public string y { get; set; }

    }

public class KakaoDocument

    {

        public string x { get; set; }

        public string y { get; set; }

    }