From 060c13f20db5a4aa4fde6742528c24ac1ab86c8c Mon Sep 17 00:00:00 2001
From: Alex Reisner <alex@alexreisner.com>
Date: Thu, 21 Apr 2011 17:53:14 -0400
Subject: [PATCH] Add support for Yandex.ru geocoder.

---
 README.rdoc                          |  8 +++--
 lib/geocoder.rb                      |  2 +-
 lib/geocoder/lookups/yandex.rb       | 30 +++++++++++++++++
 lib/geocoder/results/yandex.rb       | 40 +++++++++++++++++++++++
 test/fixtures/yandex_kremlin.json    | 48 ++++++++++++++++++++++++++++
 test/fixtures/yandex_no_results.json | 16 ++++++++++
 test/test_helper.rb                  |  9 ++++++
 7 files changed, 149 insertions(+), 4 deletions(-)
 create mode 100644 lib/geocoder/lookups/yandex.rb
 create mode 100644 lib/geocoder/results/yandex.rb
 create mode 100644 test/fixtures/yandex_kremlin.json
 create mode 100644 test/fixtures/yandex_no_results.json

diff --git a/README.rdoc b/README.rdoc
index 551c363e..4c50d882 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -233,6 +233,7 @@ Note that the result objects returned by different geocoding services all implem
 * Google: http://code.google.com/apis/maps/documentation/geocoding/#JSON
 * Yahoo: http://developer.yahoo.com/geo/placefinder/guide/responses.html
 * Geocoder.ca: (???)
+* Yandex: http://api.yandex.ru/maps/geocoder/doc/desc/concepts/response_structure.xml
 * FreeGeoIP: http://github.com/fiorix/freegeoip/blob/master/README.rst
 
 === API Keys
@@ -241,10 +242,11 @@ To use your Google API key or Yahoo app ID:
 
   Geocoder::Configuration.api_key = "..."
 
-To obtain an API key (not required):
+To obtain an API key:
 
-* Yahoo: https://developer.apps.yahoo.com/wsregapp
-* Google: http://code.google.com/apis/maps/signup.html
+* Yahoo: https://developer.apps.yahoo.com/wsregapp (not required)
+* Google: http://code.google.com/apis/maps/signup.html (not required)
+* Yandex: http://api.yandex.ru/maps/intro/concepts/intro.xml#apikey (required)
 
 === Timeout
 
diff --git a/lib/geocoder.rb b/lib/geocoder.rb
index ddca673d..33715dc0 100644
--- a/lib/geocoder.rb
+++ b/lib/geocoder.rb
@@ -125,7 +125,7 @@ module Geocoder
   # Array of valid Lookup names.
   #
   def valid_lookups
-    [:google, :yahoo, :geocoder_ca, :freegeoip]
+    [:google, :yahoo, :geocoder_ca, :yandex, :freegeoip]
   end
 
   ##
diff --git a/lib/geocoder/lookups/yandex.rb b/lib/geocoder/lookups/yandex.rb
new file mode 100644
index 00000000..66d14f5c
--- /dev/null
+++ b/lib/geocoder/lookups/yandex.rb
@@ -0,0 +1,30 @@
+require 'geocoder/lookups/base'
+require "geocoder/results/yandex"
+
+module Geocoder::Lookup
+  class Yandex < Base
+
+    private # ---------------------------------------------------------------
+
+    def results(query, reverse = false)
+      return [] unless doc = fetch_data(query, reverse)
+      if doc = doc['response']['GeoObjectCollection']
+        meta = doc['metaDataProperty']['GeocoderResponseMetaData']
+        return meta['found'].to_i > 0 ? doc['featureMember'] : []
+      else
+        warn "Yandex Geocoding API error: unexpected response format."
+        return []
+      end
+    end
+
+    def query_url(query, reverse = false)
+      params = {
+        :geocode => query,
+        :format => "json",
+        :plng => "#{Geocoder::Configuration.language}", # supports ru, uk, be
+        :key => Geocoder::Configuration.api_key
+      }
+      "http://geocode-maps.yandex.ru/1.x/?" + hash_to_query(params)
+    end
+  end
+end
diff --git a/lib/geocoder/results/yandex.rb b/lib/geocoder/results/yandex.rb
new file mode 100644
index 00000000..7f6f3281
--- /dev/null
+++ b/lib/geocoder/results/yandex.rb
@@ -0,0 +1,40 @@
+require 'geocoder/results/base'
+
+module Geocoder::Result
+  class Yandex < Base
+
+    def coordinates
+      @data['GeoObject']['Point']['pos'].split(' ').map(&:to_f)
+    end
+
+    def address(format = :full)
+      @data['GeoObject']['metaDataProperty']['GeocoderMetaData']['text']
+    end
+
+    def city
+      address_details['Locality']['LocalityName']
+    end
+
+    def country
+      address_details['CountryName']
+    end
+
+    def country_code
+      address_details['CountryNameCode']
+    end
+
+    def postal_code
+      ""
+    end
+
+    def premise_name
+      address_details['Locality']['Premise']['PremiseName']
+    end
+
+    private # ----------------------------------------------------------------
+
+    def address_details
+      @data['GeoObject']['metaDataProperty']['GeocoderMetaData']['AddressDetails']['Country']
+    end
+  end
+end
diff --git a/test/fixtures/yandex_kremlin.json b/test/fixtures/yandex_kremlin.json
new file mode 100644
index 00000000..f08b6ea3
--- /dev/null
+++ b/test/fixtures/yandex_kremlin.json
@@ -0,0 +1,48 @@
+{
+    "response": {
+        "GeoObjectCollection": {
+            "metaDataProperty": {
+                "GeocoderResponseMetaData": {
+                    "request": "Кремль, Moscow, Russia",
+                    "found": "1",
+                    "results": "10"
+                }
+            },
+            "featureMember": [
+                {
+                    "GeoObject": {
+                        "metaDataProperty": {
+                            "GeocoderMetaData": {
+                                "kind": "vegetation",
+                                "text": "Россия, Москва, Московский Кремль",
+                                "precision": "other",
+                                "AddressDetails": {
+                                    "Country": {
+                                        "CountryNameCode": "RU",
+                                        "CountryName": "Россия",
+                                        "Locality": {
+                                            "LocalityName": "Москва",
+                                            "Premise": {
+                                                "PremiseName": "Московский Кремль"
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        },
+                        "name": "Московский Кремль",
+                        "boundedBy": {
+                            "Envelope": {
+                                "lowerCorner": "37.584182 55.733361",
+                                "upperCorner": "37.650064 55.770517"
+                            }
+                        },
+                        "Point": {
+                            "pos": "37.617123 55.751943"
+                        }
+                    }
+                }
+            ]
+        }
+    }
+}
diff --git a/test/fixtures/yandex_no_results.json b/test/fixtures/yandex_no_results.json
new file mode 100644
index 00000000..66456115
--- /dev/null
+++ b/test/fixtures/yandex_no_results.json
@@ -0,0 +1,16 @@
+{
+    "response": {
+        "GeoObjectCollection": {
+            "metaDataProperty": {
+                "GeocoderResponseMetaData": {
+                    "request": "blah",
+                    "found": "0",
+                    "results": "10"
+                }
+            },
+            "featureMember": [
+
+            ]
+        }
+    }
+}
diff --git a/test/test_helper.rb b/test/test_helper.rb
index d2a6e51b..9e48f988 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -80,6 +80,15 @@ module Geocoder
       end
     end
 
+    class Yandex < Base
+      private #-----------------------------------------------------------------
+      def fetch_raw_data(query, reverse = false)
+        raise TimeoutError if query == "timeout"
+        file = query == "no results" ? :no_results : :kremlin
+        read_fixture "yandex_#{file}.json"
+      end
+    end
+
     class GeocoderCa < Base
       private #-----------------------------------------------------------------
       def fetch_raw_data(query, reverse = false)
-- 
GitLab