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; }
}