Skip to content
Browse files

added yandex geocoder

  • Loading branch information...
1 parent 3352493 commit 56b6a438749a3447fb72c813dd1a236d692e8310 @mokevnin mokevnin committed Sep 10, 2011
View
1 lib/graticule.rb
@@ -12,6 +12,7 @@
require 'graticule/geocoder/base'
require 'graticule/geocoder/bogus'
require 'graticule/geocoder/google'
+require 'graticule/geocoder/yandex'
require 'graticule/geocoder/host_ip'
require 'graticule/geocoder/multi'
require 'graticule/geocoder/yahoo'
View
2 lib/graticule/cli.rb
@@ -29,7 +29,7 @@ def self.start(args, out = STDOUT)
opts.separator ""
opts.separator "Options: "
- opts.on("-s service", %w(yahoo google geocoder_us metacarta), "--service service", "Geocoding service") do |service|
+ opts.on("-s service", %w(yahoo google yandex geocoder_us metacarta), "--service service", "Geocoding service") do |service|
options[:service] = service
end
View
131 lib/graticule/geocoder/yandex.rb
@@ -0,0 +1,131 @@
+# encoding: UTF-8
+module Graticule #:nodoc:
+ module Geocoder #:nodoc:
+
+ # First you need a Yandex Maps API key. You can register for one here:
+ # http://api.yandex.ru/maps/form.xml
+ #
+ # gg = Graticule.service(:yandex).new(MAPS_API_KEY)
+ # location = gg.locate 'Россия, Москва, ул. Моховая, д.18'
+ # p location.coordinates
+ # #=> [37.612281, 55.753342]
+ #
+ class Yandex < Base
+ # http://api.yandex.ru/maps/geocoder/doc/desc/concepts/input_params.xml
+ # http://api.yandex.ru/maps/geocoder/doc/desc/concepts/response_structure.xml
+
+ PRECISION = {
+ :country => Precision::Country, # Country level accuracy.
+ :province => Precision::Region, # Region (state, province, prefecture, etc.) level accuracy.
+ :area => Precision::Region, # Sub-region (county, municipality, etc.) level accuracy.
+ :locality => Precision::Locality, # Town (city, village) level accuracy.
+ :metro => Precision::Street, # Street level accuracy.
+ :street => Precision::Street, # Intersection level accuracy.
+ :house => Precision::Address, # Address level accuracy.
+ }.stringify_keys
+
+ def initialize(key)
+ @key = key
+ @url = URI.parse 'http://geocode-maps.yandex.ru/1.x/'
+ end
+
+ # Locates +address+ returning a Location
+ def locate(address)
+ get :geocode => address.is_a?(String) ? address : location_from_params(address).to_s
+ end
+
+ private
+
+ class GeocoderMetaData
+ include HappyMapper
+
+ tag 'GeocoderMetaData'
+ namespace 'http://maps.yandex.ru/geocoder/1.x'
+
+ element :kind, String, :tag => 'kind'
+ end
+
+ class FeatureMember
+ include HappyMapper
+
+ tag 'featureMember'
+ namespace 'http://www.opengis.net/gml'
+
+ has_one :geocoder_meta_data, GeocoderMetaData
+
+ attr_reader :longitude, :latitude
+
+ element :coordinates, String, :tag => 'pos', :deep => true
+
+ with_options :deep => true, :namespace => 'urn:oasis:names:tc:ciq:xsdschema:xAL:2.0' do |map|
+ map.element :street, String, :tag => 'ThoroughfareName'
+ map.element :locality, String, :tag => 'LocalityName'
+ map.element :region, String, :tag => 'AdministrativeAreaName'
+ map.element :postal_code, String, :tag => 'PostalCodeNumber'
+ map.element :country, String, :tag => 'CountryNameCode'
+ end
+
+ def coordinates=(coordinates)
+ @longitude, @latitude = coordinates.split(' ').map { |v| v.to_f }
+ end
+
+ def precision
+ PRECISION[geocoder_meta_data.kind] || :unknown
+ end
+ end
+
+ class Error
+ include HappyMapper
+
+ tag 'error'
+ element :status, Integer, :tag => 'status'
+ element :message, String, :tag => 'message'
+ end
+
+ class Response
+ include HappyMapper
+
+ tag 'GeoObjectCollection'
+ has_many :feature_members, FeatureMember
+
+ def status
+ 200
+ end
+ end
+
+ def prepare_response(xml)
+ Response.parse(xml, :single => true) || Error.parse(xml, :single => true)
+ end
+
+ def parse_response(response) #:nodoc:
+ result = response.feature_members.first
+ Location.new(
+ :latitude => result.latitude,
+ :longitude => result.longitude,
+ :street => result.street,
+ :locality => result.locality,
+ :country => result.country,
+ :precision => result.precision
+ )
+ end
+
+ # Extracts and raises an error from +xml+, if any.
+ def check_error(response) #:nodoc:
+ case response.status
+ when 200 then # ignore, ok
+ when 401 then
+ raise CredentialsError, response.message
+ else
+ raise Error, response.message
+ end
+ end
+
+ # Creates a URL from the Hash +params+.
+ # sets the output type to 'xml'.
+ def make_url(params) #:nodoc:
+ super params.merge(:key => @key)
+ end
+ end
+ end
+end
+
View
5 test/fixtures/responses/yandex/badkey.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<error>
+ <status>401</status>
+ <message>invalid key</message>
+</error>
View
204 test/fixtures/responses/yandex/success.xml
@@ -0,0 +1,204 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ymaps xmlns="http://maps.yandex.ru/ymaps/1.x" xmlns:x="http://www.yandex.ru/xscript" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <GeoObjectCollection>
+ <metaDataProperty xmlns="http://www.opengis.net/gml">
+ <GeocoderResponseMetaData xmlns="http://maps.yandex.ru/geocoder/1.x">
+ <request>Россия, Москва, ул. Моховая, д.18</request>
+ <found>5</found>
+ <results>10</results>
+ </GeocoderResponseMetaData>
+ </metaDataProperty>
+ <featureMember xmlns="http://www.opengis.net/gml">
+ <GeoObject xmlns="http://maps.yandex.ru/ymaps/1.x">
+ <metaDataProperty xmlns="http://www.opengis.net/gml">
+ <GeocoderMetaData xmlns="http://maps.yandex.ru/geocoder/1.x">
+ <kind>house</kind>
+ <text>Россия, Москва, Моховая улица, 18</text>
+ <precision>exact</precision>
+ <AddressDetails xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0">
+ <Country>
+ <CountryNameCode>RU</CountryNameCode>
+ <CountryName>Россия</CountryName>
+ <Locality>
+ <LocalityName>Москва</LocalityName>
+ <Thoroughfare>
+ <ThoroughfareName>Моховая улица</ThoroughfareName>
+ <Premise>
+ <PremiseNumber>18</PremiseNumber>
+ </Premise>
+ </Thoroughfare>
+ </Locality>
+ </Country>
+ </AddressDetails>
+ </GeocoderMetaData>
+ </metaDataProperty>
+ <name xmlns="http://www.opengis.net/gml">Моховая улица, 18</name>
+ <boundedBy xmlns="http://www.opengis.net/gml">
+ <Envelope>
+ <lowerCorner>37.608176 55.751026</lowerCorner>
+ <upperCorner>37.616387 55.755657</upperCorner>
+ </Envelope>
+ </boundedBy>
+ <Point xmlns="http://www.opengis.net/gml">
+ <pos>37.612281 55.753342</pos>
+ </Point>
+ </GeoObject>
+ </featureMember>
+ <featureMember xmlns="http://www.opengis.net/gml">
+ <GeoObject xmlns="http://maps.yandex.ru/ymaps/1.x">
+ <metaDataProperty xmlns="http://www.opengis.net/gml">
+ <GeocoderMetaData xmlns="http://maps.yandex.ru/geocoder/1.x">
+ <kind>house</kind>
+ <text>Россия, Московская область, Орехово-Зуево, Моховая улица, 16</text>
+ <precision>near</precision>
+ <AddressDetails xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0">
+ <Country>
+ <CountryNameCode>RU</CountryNameCode>
+ <CountryName>Россия</CountryName>
+ <AdministrativeArea>
+ <AdministrativeAreaName>Московская область</AdministrativeAreaName>
+ <Locality>
+ <LocalityName>Орехово-Зуево</LocalityName>
+ <Thoroughfare>
+ <ThoroughfareName>Моховая улица</ThoroughfareName>
+ <Premise>
+ <PremiseNumber>16</PremiseNumber>
+ </Premise>
+ </Thoroughfare>
+ </Locality>
+ </AdministrativeArea>
+ </Country>
+ </AddressDetails>
+ </GeocoderMetaData>
+ </metaDataProperty>
+ <name xmlns="http://www.opengis.net/gml">Моховая улица, 16</name>
+ <boundedBy xmlns="http://www.opengis.net/gml">
+ <Envelope>
+ <lowerCorner>38.938195 55.805402</lowerCorner>
+ <upperCorner>38.946405 55.810026</upperCorner>
+ </Envelope>
+ </boundedBy>
+ <Point xmlns="http://www.opengis.net/gml">
+ <pos>38.942300 55.807714</pos>
+ </Point>
+ </GeoObject>
+ </featureMember>
+ <featureMember xmlns="http://www.opengis.net/gml">
+ <GeoObject xmlns="http://maps.yandex.ru/ymaps/1.x">
+ <metaDataProperty xmlns="http://www.opengis.net/gml">
+ <GeocoderMetaData xmlns="http://maps.yandex.ru/geocoder/1.x">
+ <kind>house</kind>
+ <text>Россия, Московская область, Шатурский район, Шатура, Моховая улица, 18</text>
+ <precision>exact</precision>
+ <AddressDetails xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0">
+ <Country>
+ <CountryNameCode>RU</CountryNameCode>
+ <CountryName>Россия</CountryName>
+ <AdministrativeArea>
+ <AdministrativeAreaName>Московская область</AdministrativeAreaName>
+ <SubAdministrativeArea>
+ <SubAdministrativeAreaName>Шатурский район</SubAdministrativeAreaName>
+ <Locality>
+ <LocalityName>Шатура</LocalityName>
+ <Thoroughfare>
+ <ThoroughfareName>Моховая улица</ThoroughfareName>
+ <Premise>
+ <PremiseNumber>18</PremiseNumber>
+ </Premise>
+ </Thoroughfare>
+ </Locality>
+ </SubAdministrativeArea>
+ </AdministrativeArea>
+ </Country>
+ </AddressDetails>
+ </GeocoderMetaData>
+ </metaDataProperty>
+ <name xmlns="http://www.opengis.net/gml">Моховая улица, 18</name>
+ <boundedBy xmlns="http://www.opengis.net/gml">
+ <Envelope>
+ <lowerCorner>39.551106 55.562490</lowerCorner>
+ <upperCorner>39.559317 55.567143</upperCorner>
+ </Envelope>
+ </boundedBy>
+ <Point xmlns="http://www.opengis.net/gml">
+ <pos>39.555211 55.564816</pos>
+ </Point>
+ </GeoObject>
+ </featureMember>
+ <featureMember xmlns="http://www.opengis.net/gml">
+ <GeoObject xmlns="http://maps.yandex.ru/ymaps/1.x">
+ <metaDataProperty xmlns="http://www.opengis.net/gml">
+ <GeocoderMetaData xmlns="http://maps.yandex.ru/geocoder/1.x">
+ <kind>street</kind>
+ <text>Россия, Московская область, Дубна, Моховая улица</text>
+ <precision>street</precision>
+ <AddressDetails xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0">
+ <Country>
+ <CountryNameCode>RU</CountryNameCode>
+ <CountryName>Россия</CountryName>
+ <AdministrativeArea>
+ <AdministrativeAreaName>Московская область</AdministrativeAreaName>
+ <Locality>
+ <LocalityName>Дубна</LocalityName>
+ <Thoroughfare>
+ <ThoroughfareName>Моховая улица</ThoroughfareName>
+ </Thoroughfare>
+ </Locality>
+ </AdministrativeArea>
+ </Country>
+ </AddressDetails>
+ </GeocoderMetaData>
+ </metaDataProperty>
+ <name xmlns="http://www.opengis.net/gml">Моховая улица</name>
+ <boundedBy xmlns="http://www.opengis.net/gml">
+ <Envelope>
+ <lowerCorner>37.200098 56.746648</lowerCorner>
+ <upperCorner>37.204284 56.748163</upperCorner>
+ </Envelope>
+ </boundedBy>
+ <Point xmlns="http://www.opengis.net/gml">
+ <pos>37.200745 56.746949</pos>
+ </Point>
+ </GeoObject>
+ </featureMember>
+ <featureMember xmlns="http://www.opengis.net/gml">
+ <GeoObject xmlns="http://maps.yandex.ru/ymaps/1.x">
+ <metaDataProperty xmlns="http://www.opengis.net/gml">
+ <GeocoderMetaData xmlns="http://maps.yandex.ru/geocoder/1.x">
+ <kind>street</kind>
+ <text>Россия, Московская область, Озерский район, Озеры, улица Моховая</text>
+ <precision>street</precision>
+ <AddressDetails xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0">
+ <Country>
+ <CountryNameCode>RU</CountryNameCode>
+ <CountryName>Россия</CountryName>
+ <AdministrativeArea>
+ <AdministrativeAreaName>Московская область</AdministrativeAreaName>
+ <SubAdministrativeArea>
+ <SubAdministrativeAreaName>Озерский район</SubAdministrativeAreaName>
+ <Locality>
+ <LocalityName>Озеры</LocalityName>
+ <Thoroughfare>
+ <ThoroughfareName>улица Моховая</ThoroughfareName>
+ </Thoroughfare>
+ </Locality>
+ </SubAdministrativeArea>
+ </AdministrativeArea>
+ </Country>
+ </AddressDetails>
+ </GeocoderMetaData>
+ </metaDataProperty>
+ <name xmlns="http://www.opengis.net/gml">улица Моховая</name>
+ <boundedBy xmlns="http://www.opengis.net/gml">
+ <Envelope>
+ <lowerCorner>38.539415 54.859891</lowerCorner>
+ <upperCorner>38.539450 54.861559</upperCorner>
+ </Envelope>
+ </boundedBy>
+ <Point xmlns="http://www.opengis.net/gml">
+ <pos>38.539415 54.859891</pos>
+ </Point>
+ </GeoObject>
+ </featureMember>
+ </GeoObjectCollection>
+</ymaps>
View
42 test/graticule/geocoder/yandex_test.rb
@@ -0,0 +1,42 @@
+# encoding: UTF-8
+require 'test_helper'
+
+module Graticule
+ module Geocoder
+ class YandexTest < Test::Unit::TestCase
+ def setup
+ URI::HTTP.responses = []
+ URI::HTTP.uris = []
+ @geocoder = Yandex.new('APP_ID')
+ end
+
+ def test_success
+ return unless prepare_response(:success)
+
+ location = Location.new(
+ :street => "Моховая улица",
+ :locality => "Москва",
+ :country => "RU",
+ :longitude => 37.612281,
+ :latitude => 55.753342,
+ :precision => :address
+ )
+ assert_equal location, @geocoder.locate('Россия, Москва, ул. Моховая, д.18')
+ end
+
+ def test_bad_key
+ return unless prepare_response(:badkey)
+
+ assert_raises(CredentialsError) { @geocoder.locate('x') }
+ end
+
+ protected
+
+ def prepare_response(id = :success)
+ URI::HTTP.responses << response('yandex', id)
+ end
+
+ end
+ end
+end
+

0 comments on commit 56b6a43

Please sign in to comment.
Something went wrong with that request. Please try again.