geocoder-1.5.1/0000755000004100000410000000000013503210114013323 5ustar www-datawww-datageocoder-1.5.1/README.md0000644000004100000410000010415013503210114014603 0ustar www-datawww-dataGeocoder ======== **A complete geocoding solution for Ruby.** [![Gem Version](https://badge.fury.io/rb/geocoder.svg)](http://badge.fury.io/rb/geocoder) [![Code Climate](https://codeclimate.com/github/alexreisner/geocoder/badges/gpa.svg)](https://codeclimate.com/github/alexreisner/geocoder) [![Build Status](https://travis-ci.org/alexreisner/geocoder.svg?branch=master)](https://travis-ci.org/alexreisner/geocoder) [![GitHub Issues](https://img.shields.io/github/issues/alexreisner/geocoder.svg)](https://github.com/alexreisner/geocoder/issues) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) Key features: * Forward and reverse geocoding, and IP address geocoding. * Connects to more than 40 APIs worldwide. * Performance-enhancing features like caching. * Advanced configuration allows different parameters and APIs to be used in different conditions. * Integrates with ActiveRecord and Mongoid. * Basic geospatial queries: search within radius (or rectangle, or ring). Compatibility: * Supports multiple Ruby versions: Ruby 2.x, and JRuby. * Supports multiple databases: MySQL, PostgreSQL, SQLite, and MongoDB (1.7.0 and higher). * Supports Rails 3, 4, and 5. If you need to use it with Rails 2 please see the `rails2` branch (no longer maintained, limited feature set). * Works very well outside of Rails, you just need to install either the `json` (for MRI) or `json_pure` (for JRuby) gem. Table of Contents ----------------- Basic Features: * [Basic Search](#basic-search) * [Geocoding Objects](#geocoding-objects) * [Geospatial Database Queries](#geospatial-database-queries) * [Geocoding HTTP Requests](#geocoding-http-requests) * [Geocoding Service ("Lookup") Configuration](#geocoding-service-lookup-configuration) Advanced Features: * [Performance and Optimization](#performance-and-optimization) * [Advanced Model Configuration](#advanced-model-configuration) * [Advanced Database Queries](#advanced-database-queries) * [Geospatial Calculations](#geospatial-calculations) * [Batch Geocoding](#batch-geocoding) * [Testing](#testing) * [Error Handling](#error-handling) * [Command Line Interface](#command-line-interface) The Rest: * [Technical Discussions](#technical-discussions) * [Troubleshooting](#troubleshooting) * [Known Issues](#known-issues) * [Reporting Issues](#reporting-issues) * [Contributing](#contributing) See Also: * [Guide to Geocoding APIs](https://github.com/alexreisner/geocoder/blob/master/README_API_GUIDE.md) (formerly part of this README) Basic Search ------------ In its simplest form, Geocoder takes an address and searches for its latitude/longitude coordinates: results = Geocoder.search("Paris") results.first.coordinates => [48.856614, 2.3522219] # latitude and longitude The reverse is possible too. Given coordinates, it finds an address: results = Geocoder.search([48.856614, 2.3522219]) results.first.address => "Hôtel de Ville, 75004 Paris, France" You can also look up the location of an IP addresses: results = Geocoder.search("172.56.21.89") results.first.coordinates => [30.267153, -97.7430608] results.first.country => "United States" **The success and accuracy of geocoding depends entirely on the API being used to do these lookups.** Most queries work fairly well with the default configuration, but every application has different needs and every API has its particular strengths and weaknesses. If you need better coverage for your application you'll want to get familiar with the large number of supported APIs, listed in the [API Guide](https://github.com/alexreisner/geocoder/blob/master/README_API_GUIDE.md). Geocoding Objects ----------------- To automatically geocode your objects: **1.** Your model must provide a method that returns an address to geocode. This can be a single attribute, but it can also be a method that returns a string assembled from different attributes (eg: `city`, `state`, and `country`). For example, if your model has `street`, `city`, `state`, and `country` attributes you might do something like this: def address [street, city, state, country].compact.join(', ') end **2.** Your model must have a way to store latitude/longitude coordinates. With ActiveRecord, add two attributes/columns (of type float or decimal) called `latitude` and `longitude`. For MongoDB, use a single field (of type Array) called `coordinates` (i.e., `field :coordinates, type: Array`). (See [Advanced Model Configuration](#advanced-model-configuration) for using different attribute names.) **3.** In your model, tell geocoder where to find the object's address: geocoded_by :address This adds a `geocode` method which you can invoke via callback: after_validation :geocode Reverse geocoding (given lat/lon coordinates, find an address) is similar: reverse_geocoded_by :latitude, :longitude after_validation :reverse_geocode With any geocoded objects, you can do the following: obj.distance_to([43.9,-98.6]) # distance from obj to point obj.bearing_to([43.9,-98.6]) # bearing from obj to point obj.bearing_from(obj2) # bearing from obj2 to obj The `bearing_from/to` methods take a single argument which can be: a `[lat,lon]` array, a geocoded object, or a geocodable address (string). The `distance_from/to` methods also take a units argument (`:mi`, `:km`, or `:nm` for nautical miles). See [Distance and Bearing](#distance-and-bearing) below for more info. ### One More Thing for MongoDB! Before you can call `geocoded_by` you'll need to include the necessary module using one of the following: include Geocoder::Model::Mongoid include Geocoder::Model::MongoMapper ### Latitude/Longitude Order in MongoDB Everywhere coordinates are passed to methods as two-element arrays, Geocoder expects them to be in the order: `[lat, lon]`. However, as per [the GeoJSON spec](http://geojson.org/geojson-spec.html#positions), MongoDB requires that coordinates be stored longitude-first (`[lon, lat]`), so internally they are stored "backwards." Geocoder's methods attempt to hide this, so calling `obj.to_coordinates` (a method added to the object by Geocoder via `geocoded_by`) returns coordinates in the conventional order: obj.to_coordinates # => [37.7941013, -122.3951096] # [lat, lon] whereas calling the object's coordinates attribute directly (`obj.coordinates` by default) returns the internal representation which is probably the reverse of what you want: obj.coordinates # => [-122.3951096, 37.7941013] # [lon, lat] So, be careful. ### Use Outside of Rails To use Geocoder with ActiveRecord and a framework other than Rails (like Sinatra or Padrino), you will need to add this in your model before calling Geocoder methods: extend Geocoder::Model::ActiveRecord Geospatial Database Queries --------------------------- ### For ActiveRecord models: To find objects by location, use the following scopes: Venue.near('Omaha, NE, US') # venues within 20 miles of Omaha Venue.near([40.71, -100.23], 50) # venues within 50 miles of a point Venue.near([40.71, -100.23], 50, units: :km) # venues within 50 kilometres of a point Venue.geocoded # venues with coordinates Venue.not_geocoded # venues without coordinates With geocoded objects you can do things like this: if obj.geocoded? obj.nearbys(30) # other objects within 30 miles obj.distance_from([40.714,-100.234]) # distance from arbitrary point to object obj.bearing_to("Paris, France") # direction from object to arbitrary point end ### For MongoDB-backed models: Please do not use Geocoder's `near` method. Instead use MongoDB's built-in [geospatial query language](https://docs.mongodb.org/manual/reference/command/geoNear/), which is faster. Mongoid also provides [a DSL](http://mongoid.github.io/en/mongoid/docs/querying.html#geo_near) for geospatial queries. Geocoding HTTP Requests ----------------------- Geocoder adds `location` and `safe_location` methods to the standard `Rack::Request` object so you can easily look up the location of any HTTP request by IP address. For example, in a Rails controller or a Sinatra app: # returns Geocoder::Result object result = request.location **The `location` method is vulnerable to trivial IP address spoofing via HTTP headers.** If that's a problem for your application, use `safe_location` instead, but be aware that `safe_location` will *not* try to trace a request's originating IP through proxy headers; you will instead get the location of the last proxy the request passed through, if any (excepting any proxies you have explicitly whitelisted in your Rack config). Note that these methods will usually return `nil` in test and development environments because things like "localhost" and "0.0.0.0" are not geocodable IP addresses. Geocoding Service ("Lookup") Configuration ------------------------------------------ Geocoder supports a variety of street and IP address geocoding services. The default lookups are `:nominatim` for street addresses and `:ipinfo_io` for IP addresses. Please see the [API Guide](https://github.com/alexreisner/geocoder/blob/master/README_API_GUIDE.md) for details on specific geocoding services (not all settings are supported by all services). To create a Rails initializer with sample configuration: rails generate geocoder:config Some common options are: # config/initializers/geocoder.rb Geocoder.configure( # street address geocoding service (default :nominatim) lookup: :yandex, # IP address geocoding service (default :ipinfo_io) ip_lookup: :maxmind, # to use an API key: api_key: "...", # geocoding service request timeout, in seconds (default 3): timeout: 5, # set default units to kilometers: units: :km, # caching (see [below](#caching) for details): cache: Redis.new, cache_prefix: "..." ) Please see [`lib/geocoder/configuration.rb`](https://github.com/alexreisner/geocoder/blob/master/lib/geocoder/configuration.rb) for a complete list of configuration options. Additionally, some lookups have their own special configuration options which are directly supported by Geocoder. For example, to specify a value for Google's `bounds` parameter: # with Google: Geocoder.search("Middletown", bounds: [[40.6,-77.9], [39.9,-75.9]]) Please see the [source code for each lookup](https://github.com/alexreisner/geocoder/tree/master/lib/geocoder/lookups) to learn about directly supported parameters. Parameters which are not directly supported can be specified using the `:params` option, which appends options to the query string of the geocoding request. For example: # Nominatim's `countrycodes` parameter: Geocoder.search("Rome", params: {countrycodes: "us,ca"}) # Google's `region` parameter: Geocoder.search("Rome", params: {region: "..."}) ### Configuring Multiple Services You can configure multiple geocoding services at once by using the service's name as a key for a sub-configuration hash, like this: Geocoder.configure( timeout: 2, cache: Redis.new, yandex: { api_key: "...", timeout: 5 }, baidu: { api_key: "..." }, maxmind: { api_key: "...", service: :omni } ) Lookup-specific settings override global settings so, in this example, the timeout for all lookups is 2 seconds, except for Yandex which is 5. Performance and Optimization ---------------------------- ### Database Indices In MySQL and Postgres, queries use a bounding box to limit the number of points over which a more precise distance calculation needs to be done. To take advantage of this optimisation, you need to add a composite index on latitude and longitude. In your Rails migration: add_index :table, [:latitude, :longitude] In MongoDB, by default, the methods `geocoded_by` and `reverse_geocoded_by` create a geospatial index. You can avoid index creation with the `:skip_index option`, for example: include Geocoder::Model::Mongoid geocoded_by :address, skip_index: true ### Avoiding Unnecessary API Requests Geocoding only needs to be performed under certain conditions. To avoid unnecessary work (and quota usage) you will probably want to geocode an object only when: * an address is present * the address has been changed since last save (or it has never been saved) The exact code will vary depending on the method you use for your geocodable string, but it would be something like this: after_validation :geocode, if: ->(obj){ obj.address.present? and obj.address_changed? } ### Caching When relying on any external service, it's always a good idea to cache retrieved data. When implemented correctly, it improves your app's response time and stability. It's easy to cache geocoding results with Geocoder -- just configure a cache store: Geocoder.configure(cache: Redis.new) This example uses Redis, but the cache store can be any object that supports these methods: * `store#[](key)` or `#get` or `#read` - retrieves a value * `store#[]=(key, value)` or `#set` or `#write` - stores a value * `store#del(url)` - deletes a value * `store#keys` - (Optional) Returns array of keys. Used if you wish to expire the entire cache (see below). Even a plain Ruby hash will work, though it's not a great choice (cleared out when app is restarted, not shared between app instances, etc). You can also set a custom prefix to be used for cache keys: Geocoder.configure(cache_prefix: "...") By default the prefix is `geocoder:` If you need to expire cached content: Geocoder::Lookup.get(Geocoder.config[:lookup]).cache.expire(:all) # expire cached results for current Lookup Geocoder::Lookup.get(:nominatim).cache.expire("http://...") # expire cached result for a specific URL Geocoder::Lookup.get(:nominatim).cache.expire(:all) # expire cached results for Google Lookup # expire all cached results for all Lookups. # Be aware that this methods spawns a new Lookup object for each Service Geocoder::Lookup.all_services.each{|service| Geocoder::Lookup.get(service).cache.expire(:all)} Do *not* include the prefix when passing a URL to be expired. Expiring `:all` will only expire keys with the configured prefix -- it will *not* expire every entry in your key/value store. For an example of a cache store with URL expiry, please see examples/autoexpire_cache.rb _Before you implement caching in your app please be sure that doing so does not violate the Terms of Service for your geocoding service._ Advanced Model Configuration ---------------------------- You are not stuck with the `latitude` and `longitude` database column names (with ActiveRecord) or the `coordinates` array (Mongo) for storing coordinates. For example: geocoded_by :address, latitude: :lat, longitude: :lon # ActiveRecord geocoded_by :address, coordinates: :coords # MongoDB For reverse geocoding, you can specify the attribute where the address will be stored. For example: reverse_geocoded_by :latitude, :longitude, address: :loc # ActiveRecord reverse_geocoded_by :coordinates, address: :street_address # MongoDB To specify geocoding parameters in your model: geocoded_by :address, params: {region: "..."} Supported parameters: `:lookup`, `:ip_lookup`, `:language`, and `:params`. You can specify an anonymous function if you want to set these on a per-request basis. For example, to use different lookups for objects in different regions: geocoded_by :address, lookup: lambda{ |obj| obj.geocoder_lookup } def geocoder_lookup if country_code == "RU" :yandex elsif country_code == "CN" :baidu else :nominatim end end ### Custom Result Handling So far we have seen examples where geocoding results are assigned automatically to predefined object attributes. However, you can skip the auto-assignment by providing a block which handles the parsed geocoding results any way you like, for example: reverse_geocoded_by :latitude, :longitude do |obj,results| if geo = results.first obj.city = geo.city obj.zipcode = geo.postal_code obj.country = geo.country_code end end after_validation :reverse_geocode Every `Geocoder::Result` object, `result`, provides the following data: * `result.latitude` - float * `result.longitude` - float * `result.coordinates` - array of the above two in the form of `[lat,lon]` * `result.address` - string * `result.city` - string * `result.state` - string * `result.state_code` - string * `result.postal_code` - string * `result.country` - string * `result.country_code` - string Most APIs return other data in addition to these globally-supported attributes. To directly access the full response, call the `#data` method of any Geocoder::Result object. See the [API Guide](https://github.com/alexreisner/geocoder/blob/master/README_API_GUIDE.md) for links to documentation for all geocoding services. ### Forward and Reverse Geocoding in the Same Model You can apply both forward and reverse geocoding to the same model (i.e. users can supply an address or coordinates and Geocoder fills in whatever's missing) but you'll need to provide two different address methods: * one for storing the fetched address (when reverse geocoding) * one for providing an address to use when fetching coordinates (forward geocoding) For example: class Venue # build an address from street, city, and state attributes geocoded_by :address_from_components # store the fetched address in the full_address attribute reverse_geocoded_by :latitude, :longitude, address: :full_address end The same goes for latitude/longitude. However, for purposes of querying the database, there can be only one authoritative set of latitude/longitude attributes for use in database queries. This is whichever you specify last. For example, here the attributes *without* the `fetched_` prefix will be authoritative: class Venue geocoded_by :address, latitude: :fetched_latitude, longitude: :fetched_longitude reverse_geocoded_by :latitude, :longitude end Advanced Database Queries ------------------------- *The following apply to ActiveRecord only. For MongoDB, please use the built-in geospatial features.* The default `near` search looks for objects within a circle. To search within a doughnut or ring use the `:min_radius` option: Venue.near("Austin, TX", 200, min_radius: 40) To search within a rectangle (note that results will *not* include `distance` and `bearing` attributes): sw_corner = [40.71, 100.23] ne_corner = [36.12, 88.65] Venue.within_bounding_box(sw_corner, ne_corner) To search for objects near a certain point where each object has a different distance requirement (which is defined in the database), you can pass a column name for the radius: Venue.near([40.71, 99.23], :effective_radius) If you store multiple sets of coordinates for each object, you can specify latitude and longitude columns to use for a search: Venue.near("Paris", 50, latitude: :secondary_latitude, longitude: :secondary_longitude) ### Distance and Bearing When you run a geospatial query, the returned objects have two attributes added: * `obj.distance` - number of miles from the search point to this object * `obj.bearing` - direction from the search point to this object Results are automatically sorted by distance from the search point, closest to farthest. Bearing is given as a number of degrees clockwise from due north, for example: * `0` - due north * `180` - due south * `90` - due east * `270` - due west * `230.1` - southwest * `359.9` - almost due north You can convert these to compass point names via provided method: Geocoder::Calculations.compass_point(355) # => "N" Geocoder::Calculations.compass_point(45) # => "NE" Geocoder::Calculations.compass_point(208) # => "SW" _Note: when running queries on SQLite, `distance` and `bearing` are provided for consistency only. They are not very accurate._ For more advanced geospatial querying, please see the [rgeo gem](https://github.com/rgeo/rgeo). Geospatial Calculations ----------------------- The `Geocoder::Calculations` module contains some useful methods: # find the distance between two arbitrary points Geocoder::Calculations.distance_between([47.858205,2.294359], [40.748433,-73.985655]) => 3619.77359999382 # in configured units (default miles) # find the geographic center (aka center of gravity) of objects or points Geocoder::Calculations.geographic_center([city1, city2, [40.22,-73.99], city4]) => [35.14968, -90.048929] See [the code](https://github.com/alexreisner/geocoder/blob/master/lib/geocoder/calculations.rb) for more! Batch Geocoding --------------- If you have just added geocoding to an existing application with a lot of objects, you can use this Rake task to geocode them all: rake geocode:all CLASS=YourModel If you need reverse geocoding instead, call the task with REVERSE=true: rake geocode:all CLASS=YourModel REVERSE=true In either case, it won't try to geocode objects that are already geocoded. The task will print warnings if you exceed the rate limit for your geocoding service. Some services enforce a per-second limit in addition to a per-day limit. To avoid exceeding the per-second limit, you can add a `SLEEP` option to pause between requests for a given amount of time. You can also load objects in batches to save memory, for example: rake geocode:all CLASS=YourModel SLEEP=0.25 BATCH=100 To avoid exceeding per-day limits you can add a `LIMIT` option. However, this will ignore the `BATCH` value, if provided. rake geocode:all CLASS=YourModel LIMIT=1000 Testing ------- When writing tests for an app that uses Geocoder it may be useful to avoid network calls and have Geocoder return consistent, configurable results. To do this, configure the `:test` lookup and/or `:ip_lookup` Geocoder.configure(lookup: :test, ip_lookup: :test) Add stubs to define the results that will be returned: Geocoder::Lookup::Test.add_stub( "New York, NY", [ { 'coordinates' => [40.7143528, -74.0059731], 'address' => 'New York, NY, USA', 'state' => 'New York', 'state_code' => 'NY', 'country' => 'United States', 'country_code' => 'US' } ] ) With the above stub defined, any query for "New York, NY" will return the results array that follows. You can also set a default stub, to be returned when no other stub matches a given query: Geocoder::Lookup::Test.set_default_stub( [ { 'coordinates' => [40.7143528, -74.0059731], 'address' => 'New York, NY, USA', 'state' => 'New York', 'state_code' => 'NY', 'country' => 'United States', 'country_code' => 'US' } ] ) Notes: - Keys must be strings (not symbols) when calling `add_stub` or `set_default_stub`. For example `'country' =>` not `:country =>`. - To clear stubs (e.g. prior to another spec), use `Geocoder::Lookup::Test.reset`. This will clear all stubs _including the default stub_. - The stubbed result objects returned by the Test lookup do not support all the methods real result objects do. If you need to test interaction with real results it may be better to use an external stubbing tool and something like WebMock or VCR to prevent network calls. Error Handling -------------- By default Geocoder will rescue any exceptions raised by calls to a geocoding service and return an empty array. You can override this on a per-exception basis, and also have Geocoder raise its own exceptions for certain events (eg: API quota exceeded) by using the `:always_raise` option: Geocoder.configure(always_raise: [SocketError, Timeout::Error]) You can also do this to raise all exceptions: Geocoder.configure(always_raise: :all) The raise-able exceptions are: SocketError Timeout::Error Geocoder::OverQueryLimitError Geocoder::RequestDenied Geocoder::InvalidRequest Geocoder::InvalidApiKey Geocoder::ServiceUnavailable Note that only a few of the above exceptions are raised by any given lookup, so there's no guarantee if you configure Geocoder to raise `ServiceUnavailable` that it will actually be raised under those conditions (because most APIs don't return 503 when they should; you may get a `Timeout::Error` instead). Please see the source code for your particular lookup for details. Command Line Interface ---------------------- When you install the Geocoder gem it adds a `geocode` command to your shell. You can search for a street address, IP address, postal code, coordinates, etc just like you can with the Geocoder.search method for example: $ geocode 29.951,-90.081 Latitude: 29.952211 Longitude: -90.080563 Full address: 1500 Sugar Bowl Dr, New Orleans, LA 70112, USA City: New Orleans State/province: Louisiana Postal code: 70112 Country: United States Map: http://maps.google.com/maps?q=29.952211,-90.080563 There are also a number of options for setting the geocoding API, key, and language, viewing the raw JSON response, and more. Please run `geocode -h` for details. Technical Discussions --------------------- ### Distance Queries in SQLite SQLite's lack of trigonometric functions requires an alternate implementation of the `near` scope. When using SQLite, Geocoder will automatically use a less accurate algorithm for finding objects near a given point. Results of this algorithm should not be trusted too much as it will return objects that are outside the given radius, along with inaccurate distance and bearing calculations. There are few options for finding objects near a given point in SQLite without installing extensions: 1. Use a square instead of a circle for finding nearby points. For example, if you want to find points near 40.71, 100.23, search for objects with latitude between 39.71 and 41.71 and longitude between 99.23 and 101.23. One degree of latitude or longitude is at most 69 miles so divide your radius (in miles) by 69.0 to get the amount to add and subtract from your center coordinates to get the upper and lower bounds. The results will not be very accurate (you'll get points outside the desired radius), but you will get all the points within the required radius. 2. Load all objects into memory and compute distances between them using the `Geocoder::Calculations.distance_between` method. This will produce accurate results but will be very slow (and use a lot of memory) if you have a lot of objects in your database. 3. If you have a large number of objects (so you can't use approach #2) and you need accurate results (better than approach #1 will give), you can use a combination of the two. Get all the objects within a square around your center point, and then eliminate the ones that are too far away using `Geocoder::Calculations.distance_between`. Because Geocoder needs to provide this functionality as a scope, we must go with option #1, but feel free to implement #2 or #3 if you need more accuracy. ### Numeric Data Types and Precision Geocoder works with any numeric data type (e.g. float, double, decimal) on which trig (and other mathematical) functions can be performed. A summary of the relationship between geographic precision and the number of decimal places in latitude and longitude degree values is available on [Wikipedia](http://en.wikipedia.org/wiki/Decimal_degrees#Accuracy). As an example: at the equator, latitude/longitude values with 4 decimal places give about 11 metres precision, whereas 5 decimal places gives roughly 1 metre precision. Troubleshooting --------------- ### Mongoid If you get one of these errors: uninitialized constant Geocoder::Model::Mongoid uninitialized constant Geocoder::Model::Mongoid::Mongo you should check your Gemfile to make sure the Mongoid gem is listed _before_ Geocoder. If Mongoid isn't loaded when Geocoder is initialized, Geocoder will not load support for Mongoid. ### ActiveRecord A lot of debugging time can be saved by understanding how Geocoder works with ActiveRecord. When you use the `near` scope or the `nearbys` method of a geocoded object, Geocoder creates an ActiveModel::Relation object which adds some attributes (eg: distance, bearing) to the SELECT clause. It also adds a condition to the WHERE clause to check that distance is within the given radius. Because the SELECT clause is modified, anything else that modifies the SELECT clause may produce strange results, for example: * using the `pluck` method (selects only a single column) * specifying another model through `includes` (selects columns from other tables) ### Geocoding is Slow With most lookups, addresses are translated into coordinates via an API that must be accessed through the Internet. These requests are subject to the same bandwidth constraints as every other HTTP request, and will vary in speed depending on network conditions. Furthermore, many of the services supported by Geocoder are free and thus very popular. Often they cannot keep up with demand and their response times become quite bad. If your application requires quick geocoding responses you will probably need to pay for a non-free service, or--if you're doing IP address geocoding--use a lookup that doesn't require an external (network-accessed) service. For IP address lookups in Rails applications, it is generally NOT a good idea to run `request.location` during a synchronous page load without understanding the speed/behavior of your configured lookup. If the lookup becomes slow, so will your website. For the most part, the speed of geocoding requests has little to do with the Geocoder gem. Please take the time to learn about your configured lookup (links to documentation are provided above) before posting performance-related issues. ### Unexpected Responses from Geocoding Services Take a look at the server's raw response. You can do this by getting the request URL in an app console: Geocoder::Lookup.get(:nominatim).query_url(Geocoder::Query.new("...")) Replace `:nominatim` with the lookup you are using and replace `...` with the address you are trying to geocode. Then visit the returned URL in your web browser. Often the API will return an error message that helps you resolve the problem. If, after reading the raw response, you believe there is a problem with Geocoder, please post an issue and include both the URL and raw response body. You can also fetch the response in the console: Geocoder::Lookup.get(:nominatim).send(:fetch_raw_data, Geocoder::Query.new("...")) Known Issues ------------ ### Using `count` with Rails 4.1+ Due to [a change in ActiveRecord's `count` method](https://github.com/rails/rails/pull/10710) you will need to use `count(:all)` to explicitly count all columns ("*") when using a `near` scope. Using `near` and calling `count` with no argument will cause exceptions in many cases. ### Using `near` with `includes` You cannot use the `near` scope with another scope that provides an `includes` option because the `SELECT` clause generated by `near` will overwrite it (or vice versa). Instead of using `includes` to reduce the number of database queries, try using `joins` with either the `:select` option or a call to `preload`. For example: # Pass a :select option to the near scope to get the columns you want. # Instead of City.near(...).includes(:venues), try: City.near("Omaha, NE", 20, select: "cities.*, venues.*").joins(:venues) # This preload call will normally trigger two queries regardless of the # number of results; one query on hotels, and one query on administrators. # Instead of Hotel.near(...).includes(:administrator), try: Hotel.near("London, UK", 50).joins(:administrator).preload(:administrator) If anyone has a more elegant solution to this problem I am very interested in seeing it. ### Using `near` with objects close to the 180th meridian The `near` method will not look across the 180th meridian to find objects close to a given point. In practice this is rarely an issue outside of New Zealand and certain surrounding islands. This problem does not exist with the zero-meridian. The problem is due to a shortcoming of the Haversine formula which Geocoder uses to calculate distances. Reporting Issues ---------------- When reporting an issue, please list the version of Geocoder you are using and any relevant information about your application (Rails version, database type and version, etc). Please describe as specifically as you can what behavior you are seeing (eg: an error message? a nil return value?). Please DO NOT use GitHub issues to ask questions about how to use Geocoder. Sites like [StackOverflow](http://www.stackoverflow.com/) are a better forum for such discussions. Contributing ------------ Contributions are welcome via Github pull requests. If you are new to the project and looking for a way to get involved, try picking up an issue with a "beginner-task" label. Hints about what needs to be done are usually provided. For all contributions, please respect the following guidelines: * Each pull request should implement ONE feature or bugfix. If you want to add or fix more than one thing, submit more than one pull request. * Do not commit changes to files that are irrelevant to your feature or bugfix (eg: `.gitignore`). * Do not add dependencies on other gems. * Do not add unnecessary `require` statements which could cause LoadErrors on certain systems. * Remember: Geocoder needs to run outside of Rails. Don't assume things like ActiveSupport are available. * Be willing to accept criticism and work on improving your code; Geocoder is used by thousands of developers and care must be taken not to introduce bugs. * Be aware that the pull request review process is not immediate, and is generally proportional to the size of the pull request. * If your pull request is merged, please do not ask for an immediate release of the gem. There are many factors contributing to when releases occur (remember that they affect thousands of apps with Geocoder in their Gemfiles). If necessary, please install from the Github source until the next official release. Copyright (c) 2009-18 Alex Reisner, released under the MIT license. geocoder-1.5.1/bin/0000755000004100000410000000000013503210114014073 5ustar www-datawww-datageocoder-1.5.1/bin/geocode0000755000004100000410000000010413503210114015421 0ustar www-datawww-data#!/usr/bin/env ruby require 'geocoder/cli' Geocoder::Cli.run ARGV geocoder-1.5.1/CHANGELOG.md0000644000004100000410000006036313503210114015144 0ustar www-datawww-dataChangelog ========= Major changes to Geocoder for each release. Please see the Git log for complete list of changes. 1.5.1 (2019 Jan 23) ------------------- * Add support for :tencent lookup (thanks github.com/Anders-E). * Add support for :smarty_streets international API (thanks github.com/ankane). * Remove :mapzen lookup. 1.5.0 (2018 Jul 31) ------------------- * Drop support for Ruby <2.0. * Change default street address lookup from :google to :nominatim. * Cache keys no longer include API credentials. This means many entries in existing cache implementations will be invalidated. * Test lookup fixtures should now return `coordinates` and NOT `latitude`/`longitude` attributes (see #1258). This may break some people's tests. * Add support for :ip2location lookup (thanks github.com/ip2location). * Remove :ovi and :okf lookups. 1.4.9 (2018 May 27) ------------------- * Fix regression in :geoip2 lookup. * Add support for Postcodes.io lookup (thanks github.com/sledge909). 1.4.8 (2018 May 21) ------------------- * Change default IP address lookup from :freegeoip to :ipinfo_io. * Add support for :ipstack lookup (thanks github.com/Heath101). * Fix incompatibility with redis-rb gem v4.0. 1.4.7 (2018 Mar 13) ------------------- * Allow HTTP protocol for Nominatim. 1.4.6 (2018 Feb 28) ------------------- * Add support for :ipdata_co lookup (thanks github.com/roschaefer). * Update for Rails 5.2 compatibility (thanks github.com/stevenharman). 1.4.5 (2017 Nov 29) ------------------- * Add support for :pickpoint lookup (thanks github.com/cylon-v). * Add support for :db_ip_com lookup (thanks github.com/cv). * Change FreeGeoIP host to freegeoip.net. * Allow search radius to be a symbol representing a column in DB (thanks github.com/leonelgalan). * Add support for new parameters and improved error handling for several lookups. * Fix bug in SQL when searching for objects across 180th meridian. 1.4.4 (2017 May 17) ------------------- * Use HTTPS by default for :freegeoip (thanks github.com/mehanoid). * Add support for :amap lookup (thanks github.com/pzgz). 1.4.3 (2017 Feb 7) ------------------- * Add :google_places_search lookup (thanks github.com/waruboy). 1.4.2 (2017 Jan 31) ------------------- * Fix bug that caused Model.near to return an incorrect query in some cases. 1.4.1 (2016 Dec 2) ------------------- * Add :location_iq lookup (thanks github.com/aleemuddin13 and glsignal). * Add :ban_data_gouv_fr lookup (thanks github.com/JulianNacci). * Fix :mapbox results when server returns no context (thanks github.com/jcmuller). * Deprecate methods in Geocoder::Calculations: to_kilometers, to_miles, to_nautical_miles, mi_in_km, km_in_mi, km_in_nm, nm_in_km. 1.4.0 (2016 Sep 8) ------------------- * Only consider an object geocoded if both lat and lon are present (previously was sufficient to have only one of the two) (thanks github.com/mltsy). * Add support in :geocodio lookup for Canadian addresses (thanks github.com/bolandrm). * Add support for SQLite extensions, if present (thanks github.com/stevecj). * Always include Geocoder in Rack::Request, if defined (thanks github.com/wjordan). 1.3.7 (2016 Jun 9) ------------------- * Fix Ruby 1.9, 2.0 incompatibility (thanks github.com/ebouchut). * Update SmartyStreets zipcode API endpoint (thanks github.com/jeffects). * Catch network errors (thanks github.com/sas1ni69). 1.3.6 (2016 May 31) ------------------- * Fix Sinatra support broken in 1.3.5. 1.3.5 (2016 May 27) ------------------- * Fix Rails 5 ActionDispatch include issue (thanks github.com/alepore). * Fix bug that caused :esri lookup to ignore certain configured parameters (thanks github.com/aaronpk). * Add reverse geocoding support to :pelias/:mapzen (thanks github.com/shinyaK14). * Add support for custom host with :telize (thanks github.com/jwaldrip). * Change the way :esri lookup generates cache keys for improved performance (thanks github.com/aaronpk). * Improve HTTPS connections with :google (thanks github.com/jlhonora). 1.3.4 (2016 Apr 14) ------------------- * Update :freegeoip host (old one is down). * Add IP lookup :ipapi_com (thanks github.com/piotrgorecki). 1.3.3 (2016 Apr 4) ------------------ * Fix incorrect gem version number. 1.3.2 (2016 Apr 1) ------------------ * Remove :yahoo lookup (service was discontinued Mar 31) (thanks github.com/galiat). * Add support for LatLon.io service (thanks github.com/evanmarks). * Add support for IpInfo.io service (thanks github.com/rehan, akostyuk). * Add support for Pelias/Mapzen service (thanks github.com/RealScout). 1.3.1 (2016 Feb 20) ------------------- * Warn about upcoming discontinuation of :yahoo lookup (thanks github.com/galiat). * Add #viewport method to results that return viewport data (thanks github.com/lime). 1.3.0 (2016 Jan 31) ------------------- * Lazy load lookups to reduce memory footprint (thanks github.com/TrangPham). * Add :geoportail_lu lookup (Luxembourg only) (thanks github.com/mdebo). * Maxmind local query performance improvement (thanks github.com/vojtad). * Remove deprecated Mongo near query methods (please use Mongo-native methods instead). 1.2.14 (2015 Dec 27) -------------------- * Fix bug in :geoip2 lookup (thanks github.com/mromulus). 1.2.13 (2015 Dec 15) -------------------- * Update :telize IP lookup to reflect new URL (thanks github.com/jfredrickson). * Add reverse geocode rake task (thanks github.com/FanaHOVA). * Fix reversed coordinates array with Mapbox (thanks github.com/marcusat). * Fix missing city name in some cases with ESRI (thanks github.com/roybotnik). * Prevent re-opening of DB file on every read with :geoip2 (thanks github.com/oogali). 1.2.12 (2015 Oct 29) -------------------- * Fix Ruby 1.9.3 incompatibility (remove non-existent timeout classes) (thanks github.com/roychri). 1.2.11 (2015 Sep 10) -------------------- * Fix load issue on Ruby 1.9.3. 1.2.10 (2015 Sep 7) ------------------- * Force Yandex to use HTTPS (thanks github.com/donbobka). * Force :google to use HTTPS if API key set. * Fix out-of-the-box verbosity issues (GH #881). * Improve timeout mechanism and add exception Geocoder::LookupTimeout (thanks github.com/ankane). * Deprecate .near and #nearbys for MongoDB-backed models. 1.2.9 (2015 Jun 12) ------------------- * Don't cache unsuccessful responses from Bing (thanks github.com/peteb). * Show API response when not valid JSON. * Log each API request. * Force all SmartyStreets requests to use HTTPS. 1.2.8 (2015 Mar 21) ------------------- * Add :maxmind_geoip2 lookup (thanks github.com/TrangPham). * Add ability to force/specify query type (street or IP address) (thanks github.com/TrangPham). * Add :basic_auth configuration (thanks github.com/TrangPham). * Add `safe_location` method for Rails controllers (thanks github.com/edslocomb). * Add :logger configuration (thanks github.com/TrangPham). * Improve error condition handling with Bing (thanks github.com/TrangPham). 1.2.7 (2015 Jan 24) ------------------- * DROP SUPPORT for Ruby 1.9.2. * Use UTF-8 encoding for maxmind_local results (thanks github.com/ellmo). * Update freegeoip response handling (thanks github.com/hosamaly). * Update nominatim response handling (thanks github.com/jsantos). * Update yandex response handling (thanks github.com/wfleming). * Update geocodio response handling (thanks github.com/getsidewalk). * Add ability to raise exception when response parsing fails (thanks github.com/spiderpug). * Fix double-loading of Railtie (thanks github.com/wfleming and zhouguangming). 1.2.6 (2014 Nov 8) ------------------ * Add :geoip2 lookup (thanks github.com/ChristianHoj). * Add :okf lookup (thanks github.com/kakoni). * Add :postcode_anywhere_uk lookup (thanks github.com/rob-murray). * Properly detect IPv6 addresses (thanks github.com/sethherr and github.com/ignatiusreza). 1.2.5 (2014 Sep 12) ------------------- * Fix bugs in :opencagedata lookup (thanks github.com/duboff and kayakyakr). * Allow language to be set in model configuration (thanks github.com/viniciusnz). * Optimize lookup queries when using MaxMind Local (thanks github.com/gersmann). 1.2.4 (2014 Aug 12) ------------------- * Add ability to specify lat/lon column names with .near scope (thanks github.com/switzersc). * Add OpenCageData geocoder (thanks github.com/mtmail). * Remove CloudMade geocoder. 1.2.3 (2014 Jul 11) ------------------- * Add Telize IP address lookup (thanks github.com/lukeroberts1990). * Fix bug in Bing reverse geocoding (thanks github.com/nickelser). 1.2.2 (2014 Jun 12) ------------------- * Add ability to specify language per query (thanks github.com/mkristian). * Handle Errno::ECONNREFUSED exceptions like TimeoutError exceptions. * Switch to 'unstructured' query format for Bing API (thanks github.com/lukewendling). 1.2.1 (2014 May 12) ------------------- * Fix: correctly handle encoding of MaxMind API responses (thanks github.com/hydrozen, gonzoyumo). * Fixes to :maxmind_local database structure (thanks github.com/krakatoa). 1.2.0 (2014 Apr 16) ------------------- * DROP SUPPORT for Ruby 1.8.x. * Add :here lookup (thanks github.com/christoph-buente). * Add :cloudmade lookup (thanks github.com/spoptchev). * Add :smarty_streets lookup (thanks github.com/drinks). * Add :maxmind_local IP lookup (thanks github.com/fernandomm). * Add :baidu_ip lookup (thanks github.com/yonggu). * Add :geocodio lookup (thanks github.com/dblock). * Add :lookup option to `Geocoder.search` and `geocoded_by` (thanks github.com/Bonias). * Add support for :maxmind_local on JRuby via jgeoip gem (thanks github.com/gxbe). * Add support for using :maxmind_local with an SQL database, including Rake tasks for downloading CSV data and populating a local DB. * Add support for character encodings based on Content-type header (thanks github.com/timaro). * Add :min_radius option to `near` scope (thanks github.com/phallstrom). * Fix: Yandex city attribute caused exception with certain responses (thanks github.com/dblock). * Change name of MapQuest config option from :licensed to :open and revert default behavior to be MapQuest data (not OpenStreetMaps). * Reduce number of Ruby warnings (thanks github.com/exviva). 1.1.9 (2013 Dec 11) ------------------- * DEPRECATED support for Ruby 1.8.x. Will be dropped in a future version. * Require API key for MapQuest (thanks github.com/robdiciuccio). * Add support for geocoder.us and HTTP basic auth (thanks github.com/komba). * Add support for Data Science Toolkit lookup (thanks github.com/ejhayes). * Add support for Baidu (thanks github.com/mclee). * Add Geocoder::Calculations.random_point_near method (thanks github.com/adambray). * Fix: #nearbys method with Mongoid (thanks github.com/pascalbetz). * Fix: bug in FreeGeoIp lookup that was preventing exception from being raised when configured cache was unavailable. 1.1.8 (2013 Apr 22) ------------------- * Fix bug in ESRI lookup that caused an exception on load in environments without rack/utils. 1.1.7 (2013 Apr 21) ------------------- * Add support for Ovi/Nokia API (thanks github.com/datenimperator). * Add support for ESRI API (thanks github.com/rpepato). * Add ability to omit distance and bearing from SQL select clause (thanks github.com/nicolasdespres). * Add support for caches that use read/write methods (thanks github.com/eskil). * Add support for nautical miles (thanks github.com/vanboom). * Fix: bug in parsing of MaxMind responses. * Fix: bugs in query regular expressions (thanks github.com/boone). * Fix: various bugs in MaxMind implementation. * Fix: don't require a key for MapQuest. * Fix: bug in handling of HTTP_X_FORWARDED_FOR header (thanks github.com/robdimarco). 1.1.6 (2012 Dec 24) ------------------- * Major changes to configuration syntax which allow for API-specific config options. Old config syntax is now DEPRECATED. * Add support for MaxMind API (thanks github.com/gonzoyumo). * Add optional Geocoder::InvalidApiKey exception for bad API credentials (Yahoo, Yandex, Bing, and Maxmind). Warn when bad key and exception not set in Geocoder.configure(:always_raise => [...]). * Add support for X-Real-IP and X-Forwarded-For headers (thanks github.com/konsti). * Add support for custom Nominatim host config: Geocoder.configure(:nominatim => {:host => "..."}). * Raise exception when required API key is missing or incorrect format. * Add support for Google's :region and :components parameters (thanks to github.com/tomlion). * Fix: string escaping bug in OAuth lib (thanks github.com/m0thman). * Fix: configured units were not always respected in SQL queries. * Fix: in #nearbys, don't try to exclude self if not yet persisted. * Fix: bug with cache stores that provided #delete but not #del. * Change #nearbys so that it returns nil instead of [] when object is not geocoded. 1.1.5 (2012 Nov 9) ------------------ * Replace support for old Yahoo Placefinder with Yahoo BOSS (thanks github.com/pwoltman). * Add support for actual Mapquest API (was previously just a proxy for Nominatim), including the paid service (thanks github.com/jedschneider). * Add support for :select => :id_only option to near scope. * Treat a given query as blank (don't do a lookup) if coordinates are given but latitude or longitude is nil. * Speed up 'near' queries by adding bounding box condition (thanks github.com/mlandauer). * Fix: don't redefine Object#hash in Yahoo result object (thanks github.com/m0thman). 1.1.4 (2012 Oct 2) ------------------ * Deprecate Geocoder::Result::Nominatim#class and #type methods. Use #place_class and #place_type instead. * Add support for setting arbitrary parameters in geocoding request URL. * Add support for Google's :bounds parameter (thanks to github.com/rosscooperman and github.com/peterjm for submitting suggestions). * Add support for :select => :geo_only option to near scope (thanks github.com/gugl). * Add ability to omit ORDER BY clause from .near scope (pass option :order => false). * Fix: error on Yahoo lookup due to API change (thanks github.com/kynesun). * Fix: problem with Mongoid field aliases not being respected. * Fix: :exclude option to .near scope when primary key != :id (thanks github.com/smisml). * Much code refactoring (added Geocoder::Query class and Geocoder::Sql module). 1.1.3 (2012 Aug 26) ------------------- * Add support for Mapquest geocoding service (thanks github.com/razorinc). * Add :test lookup for easy testing of apps using Geocoder (thanks github.com/mguterl). * Add #precision method to Yandex results (thanks github.com/gemaker). * Add support for raising :all exceptions (thanks github.com/andyvb). * Add exceptions for certain Google geocoder responses (thanks github.com/andyvb). * Add Travis-CI integration (thanks github.com/petergoldstein). * Fix: unit config was not working with SQLite (thanks github.com/balvig). * Fix: get tests to pass under Jruby (thanks github.com/petergoldstein). * Fix: bug in distance_from_sql method (error occurred when coordinates not found). * Fix: incompatibility with Mongoid 3.0.x (thanks github.com/petergoldstein). 1.1.2 (2012 May 24) ------------------- * Add ability to specify default units and distance calculation method (thanks github.com/abravalheri). * Add new (optional) configuration syntax (thanks github.com/abravalheri). * Add support for cache stores that provide :get and :set methods. * Add support for custom HTTP request headers (thanks github.com/robotmay). * Add Result#cache_hit attribute (thanks github.com/s01ipsist). * Fix: rake geocode:all wasn't properly loading namespaced classes. * Fix: properly recognize IP addresses with ::ffff: prefix (thanks github.com/brian-ewell). * Fix: avoid exception during calculations when coordinates not known (thanks github.com/flori). 1.1.1 (2012 Feb 16) ------------------- * Add distance_from_sql class method to geocoded class (thanks github.com/dwilkie). * Add OverQueryLimitError and raise when relevant for Google lookup. * Fix: don't cache API data if response indicates an error. * Fix: within_bounding_box now uses correct lat/lon DB columns (thanks github.com/kongo). * Fix: error accessing city in some cases with Yandex result (thanks github.com/kor6n and sld). 1.1.0 (2011 Dec 3) ------------------ * A block passed to geocoded_by is now always executed, even if the geocoding service returns no results. This means you need to make sure you have results before trying to assign data to your object. * Fix issues with joins and row counts (issues #49, 86, and 108) by not using GROUP BY clause with ActiveRecord scopes. * Fix incorrect object ID when join used (fixes issue #140). * Fix calculation of bounding box which spans 180th meridian (thanks github.com/hwuethrich). * Add within_bounding_box scope for ActiveRecord-based models (thanks github.com/gavinhughes and dbloete). * Add option to raise Geocoder::OverQueryLimitError for Google geocoding service. * Add support for Nominatim geocoding service (thanks github.com/wranglerdriver). * Add support for API key to Geocoder.ca geocoding service (thanks github.com/ryanLonac). * Add support for state to Yandex results (thanks github.com/tipugin). 1.0.5 (2011 Oct 26) ------------------- * Fix error with `rake assets:precompile` (thanks github.com/Sush). * Fix HTTPS support (thanks github.com/rsanheim). * Improve cache interface. 1.0.4 (2011 Sep 18) ------------------- * Remove klass method from rake task, which could conflict with app methods (thanks github.com/mguterl). 1.0.3 (2011 Sep 17) ------------------- * Add support for Google Premier geocoding service (thanks github.com/steveh). * Update Google API URL (thanks github.com/soorajb). * Allow rescue from timeout with FreeGeoIP (thanks github.com/lukeledet). * Fix: rake assets:precompile (Rails 3.1) not working in some situations. * Fix: stop double-adjusting units when using kilometers (thanks github.com/hairyheron). 1.0.2 (2011 June 25) -------------------- * Add support for MongoMapper (thanks github.com/spagalloco). * Fix: user-specified coordinates field wasn't working with Mongoid (thanks github.com/thisduck). * Fix: invalid location given to near scope was returning all results (Active Record) or error (Mongoid) (thanks github.com/ogennadi). 1.0.1 (2011 May 17) ------------------- * Add option to not rescue from certain exceptions (thanks github.com/ahmedrb). * Fix STI child/parent geocoding bug (thanks github.com/ogennadi). * Other bugfixes. 1.0.0 (2011 May 9) ------------------ * Add command line interface. * Add support for local proxy (thanks github.com/Olivier). * Add support for Yandex.ru geocoding service. * Add support for Bing geocoding service (thanks github.com/astevens). * Fix single table inheritance bug (reported by github.com/enrico). * Fix bug when Google result supplies no city (thanks github.com/jkeen). 0.9.13 (2011 Apr 11) -------------------- * Fix "can't find special index: 2d" error when using Mongoid with Ruby 1.8. 0.9.12 (2011 Apr 6) ------------------- * Add support for Mongoid. * Add bearing_to/from methods to geocoded objects. * Improve SQLite's distance calculation heuristic. * Fix: Geocoder::Calculations.geographic_center was modifying its argument in-place (reported by github.com/joelmats). * Fix: sort 'near' query results by distance when using SQLite. * Clean up input: search for coordinates as a string with space after comma yields zero results from Google. Now we get rid of any such space before sending the query. * DEPRECATION: Geocoder.near should not take :limit or :offset options. * DEPRECATION: Change argument format of all methods that take lat/lon as separate arguments. Now you must pass the coordinates as an array [lat,lon], but you may alternatively pass a address string (will look up coordinates) or a geocoded object (or any object that implements a to_coordinates method which returns a [lat,lon] array). 0.9.11 (2011 Mar 25) -------------------- * Add support for result caching. * Add support for Geocoder.ca geocoding service. * Add +bearing+ attribute to objects returned by geo-aware queries (thanks github.com/matellis). * Add config setting: language. * Add config settings: +use_https+, +google_api_key+ (thanks github.com/svesely). * DEPRECATION: Geocoder.search now returns an array instead of a single result. * DEPRECATION: obj.nearbys second argument is now an options hash (instead of units). Please change obj.nearbys(20, :km) to: obj.nearbys(20, :units => :km). 0.9.10 (2011 Mar 9) ------------------- * Fix broken scopes (github.com/mikepinde). * Fix broken Ruby 1.9 and JRuby compatibility (don't require json gem). 0.9.9 (2011 Mar 9) ------------------ * Add support for IP address geocoding via FreeGeoIp.net. * Add support for Yahoo PlaceFinder geocoding API. * Add support for custom geocoder data handling by passing a block to geocoded_by or reverse_geocoded_by. * Add Rack::Request#location method for geocoding user's IP address. * Change gem name to geocoder (no more rails-geocoder). * Gem now works outside of Rails. * DEPRECATION: +fetch_coordinates+ no longer takes an argument. * DEPRECATION: +fetch_address+ no longer takes an argument. * DEPRECATION: Geocoder.search now returns a single result instead of an array. * DEPRECATION: fetch_coordinates! has been superceded by +geocode+ (then save your object manually). * DEPRECATION: fetch_address! has been superceded by +reverse_geocode+ (then save your object manually). * Fix: don't die when trying to get coordinates with a nil address (github.com/zmack). 0.9.8 (2011 Feb 8) ------------------ * Include geocode:all Rake task in gem (was missing!). * Add Geocoder.search for access to Google's full response. * Add ability to configure Google connection timeout. * Emit warnings on Google connection problems and errors. * Refactor: insert Geocoder into ActiveRecord via Railtie. 0.9.7 (2011 Feb 1) ------------------ * Add reverse geocoding (+reverse_geocoded_by+). * Prevent exception (uninitialized constant Geocoder::Net) when net/http not already required (github.com/sleepycat). * Refactor: split monolithic Geocoder module into several smaller ones. 0.9.6 (2011 Jan 19) ------------------- * Fix incompatibility with will_paginate gem. * Include table names in GROUP BY clause of nearby scope to avoid ambiguity in joins (github.com/matchu). 0.9.5 (2010 Oct 15) ------------------- * Fix broken PostgreSQL compatibility (now 100% compatible). * Switch from Google's XML to JSON geocoding API. * Separate Rails 2 and Rails 3-compatible branches. * Don't allow :conditions hash in 'options' argument to 'nearbys' method (was deprecated in 0.9.3). 0.9.4 (2010 Aug 2) ------------------ * Google Maps API key no longer required (uses geocoder v3). 0.9.3 (2010 Aug 2) ------------------ * Fix incompatibility with Rails 3 RC 1. * Deprecate 'options' argument to 'nearbys' method. * Allow inclusion of 'nearbys' in Arel method chains. 0.9.2 (2010 Jun 3) ------------------ * Fix LIMIT clause bug in PostgreSQL (reported by github.com/kenzie). 0.9.1 (2010 May 4) ------------------ * Use scope instead of named_scope in Rails 3. 0.9.0 (2010 Apr 2) ------------------ * Fix bug in PostgreSQL support (caused "PGError: ERROR: column "distance" does not exist"), reported by github.com/developish. 0.8.9 (2010 Feb 11) ------------------- * Add Rails 3 compatibility. * Avoid querying Google when query would be an empty string. 0.8.8 (2009 Dec 7) ------------------ * Automatically select a less accurate but compatible distance algorithm when SQLite database detected (fixes SQLite incompatibility). 0.8.7 (2009 Nov 4) ------------------ * Added Geocoder.geographic_center method. * Replaced _get_coordinates class method with read_coordinates instance method. 0.8.6 (2009 Oct 27) ------------------- * The fetch_coordinates method now assigns coordinates to attributes (behaves like fetch_coordinates! used to) and fetch_coordinates! both assigns and saves the attributes. * Added geocode:all rake task. 0.8.5 (2009 Oct 26) ------------------- * Avoid calling deprecated method from within Geocoder itself. 0.8.4 (2009 Oct 23) ------------------- * Deprecate find_near class method in favor of +near+ named scope. 0.8.3 (2009 Oct 23) ------------------- * Update Google URL query string parameter to reflect recent changes in Google's API. 0.8.2 (2009 Oct 12) ------------------- * Allow a model's geocoder search string method to be something other than an ActiveRecord attribute. * Clean up documentation. 0.8.1 (2009 Oct 8) ------------------ * Extract XML-fetching code from Geocoder.search and place in Geocoder._fetch_xml (for ease of mocking). * Add tests for coordinate-fetching instance methods. 0.8.0 (2009 Oct 1) ------------------ First release. geocoder-1.5.1/LICENSE0000644000004100000410000000204313503210114014327 0ustar www-datawww-dataCopyright (c) 2009-11 Alex Reisner Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. geocoder-1.5.1/examples/0000755000004100000410000000000013503210114015141 5ustar www-datawww-datageocoder-1.5.1/examples/autoexpire_cache_redis.rb0000644000004100000410000000113213503210114022161 0ustar www-datawww-data# This class implements a cache with simple delegation to the Redis store, but # when it creates a key/value pair, it also sends an EXPIRE command with a TTL. # It should be fairly simple to do the same thing with Memcached. class AutoexpireCacheRedis def initialize(store, ttl = 86400) @store = store @ttl = ttl end def [](url) @store.get(url) end def []=(url, value) @store.set(url, value) @store.expire(url, @ttl) end def keys @store.keys end def del(url) @store.del(url) end end Geocoder.configure(:cache => AutoexpireCacheRedis.new(Redis.new)) geocoder-1.5.1/examples/autoexpire_cache_dalli.rb0000644000004100000410000000246213503210114022147 0ustar www-datawww-data# This class implements a cache with simple delegation to the the Dalli Memcached client # https://github.com/mperham/dalli # # A TTL is set on initialization class AutoexpireCacheDalli def initialize(store, ttl = 86400) @store = store @keys = 'GeocoderDalliClientKeys' @ttl = ttl end def [](url) res = @store.get(url) res = YAML::load(res) if res.present? res end def []=(url, value) if value.nil? del(url) else key_cache_add(url) if @store.add(url, YAML::dump(value), @ttl) end value end def keys key_cache end def del(url) key_cache_delete(url) if @store.delete(url) end private def key_cache the_keys = @store.get(@keys) if the_keys.nil? @store.add(@keys, YAML::dump([])) [] else YAML::load(the_keys) end end def key_cache_add(key) @store.replace(@keys, YAML::dump(key_cache << key)) end def key_cache_delete(key) tmp = key_cache tmp.delete(key) @store.replace(@keys, YAML::dump(tmp)) end end # Here Dalli is set up as on Heroku using the Memcachier gem. # https://devcenter.heroku.com/articles/memcachier#ruby # On other setups you might have to specify your Memcached server in Dalli::Client.new Geocoder.configure(:cache => AutoexpireCacheDalli.new(Dalli::Client.new)) geocoder-1.5.1/examples/cache_bypass.rb0000644000004100000410000000175013503210114020115 0ustar www-datawww-data# This class allows you to configure how Geocoder should treat errors that occur when # the cache is not available. # Configure it like this # config/initializers/geocoder.rb # Geocoder.configure( # :cache => Geocoder::CacheBypass.new(Redis.new) # ) # # Depending on the value of @bypass this will either # raise the exception (true) or swallow it and pretend the cache did not return a hit (false) # class Geocoder::CacheBypass def initialize(target, bypass = true) @target = target @bypass = bypass end def [](key) with_bypass { @target[key] } end def []=(key, value) with_bypass(value) { @target[key] = value } end def keys with_bypass([]) { @target.keys } end def del(key) with_bypass { @target.del(key) } end private def with_bypass(return_value_if_exception = nil, &block) begin yield rescue if @bypass return_value_if_exception else raise # reraise original exception end end end endgeocoder-1.5.1/examples/reverse_geocode_job.rb0000644000004100000410000000175013503210114021463 0ustar www-datawww-data# This class implements an ActiveJob job for performing reverse-geocoding # asynchronously. Example usage: # if @location.save && @location.address.blank? # ReverseGeocodeJob.perform_later(@location) # end # Be sure to configure the queue adapter in config/application.rb: # config.active_job.queue_adapter = :sidekiq # You can read the Rails docs for more information on configuring ActiveJob: # http://edgeguides.rubyonrails.org/active_job_basics.html class ReverseGeocodeJob < ActiveJob::Base queue_as :high def perform(location) address = address(location) if address.present? location.update(address: address) end end private def address(location) Geocoder.address(location.coordinates) rescue => exception MonitoringService.notify(exception, location: { id: location.id }) if retryable?(exception) raise exception end end def retryable?(exception) exception.is_a?(Timeout::Error) || exception.is_a?(SocketError) end end geocoder-1.5.1/lib/0000755000004100000410000000000013503210114014071 5ustar www-datawww-datageocoder-1.5.1/lib/geocoder.rb0000644000004100000410000000237713503210114016216 0ustar www-datawww-datarequire "geocoder/configuration" require "geocoder/logger" require "geocoder/kernel_logger" require "geocoder/query" require "geocoder/calculations" require "geocoder/exceptions" require "geocoder/cache" require "geocoder/request" require "geocoder/lookup" require "geocoder/ip_address" require "geocoder/models/active_record" if defined?(::ActiveRecord) require "geocoder/models/mongoid" if defined?(::Mongoid) require "geocoder/models/mongo_mapper" if defined?(::MongoMapper) module Geocoder ## # Search for information about an address or a set of coordinates. # def self.search(query, options = {}) query = Geocoder::Query.new(query, options) unless query.is_a?(Geocoder::Query) query.blank? ? [] : query.execute end ## # Look up the coordinates of the given street or IP address. # def self.coordinates(address, options = {}) if (results = search(address, options)).size > 0 results.first.coordinates end end ## # Look up the address of the given coordinates ([lat,lon]) # or IP address (string). # def self.address(query, options = {}) if (results = search(query, options)).size > 0 results.first.address end end end # load Railtie if Rails exists if defined?(Rails) require "geocoder/railtie" end geocoder-1.5.1/lib/tasks/0000755000004100000410000000000013503210114015216 5ustar www-datawww-datageocoder-1.5.1/lib/tasks/geocoder.rake0000644000004100000410000000304413503210114017652 0ustar www-datawww-datarequire "geocoder/models/mongoid" namespace :geocode do desc "Geocode all objects without coordinates." task :all => :environment do class_name = ENV['CLASS'] || ENV['class'] sleep_timer = ENV['SLEEP'] || ENV['sleep'] batch = ENV['BATCH'] || ENV['batch'] reverse = ENV['REVERSE'] || ENV['reverse'] limit = ENV['LIMIT'] || ENV['limit'] raise "Please specify a CLASS (model)" unless class_name klass = class_from_string(class_name) batch = (batch.to_i unless batch.nil?) || 1000 orm = (klass < Geocoder::Model::Mongoid) ? 'mongoid' : 'active_record' reverse = false unless reverse.to_s.downcase == 'true' geocode_record = lambda { |obj| reverse ? obj.reverse_geocode : obj.geocode obj.save sleep(sleep_timer.to_f) unless sleep_timer.nil? } scope = reverse ? klass.not_reverse_geocoded : klass.not_geocoded scope = scope.limit(limit) if limit if orm == 'mongoid' scope.each do |obj| geocode_record.call(obj) end elsif orm == 'active_record' if limit scope.each do |obj| geocode_record.call(obj) end else scope.find_each(batch_size: batch) do |obj| geocode_record.call(obj) end end end end end ## # Get a class object from the string given in the shell environment. # Similar to ActiveSupport's +constantize+ method. # def class_from_string(class_name) parts = class_name.split("::") constant = Object parts.each do |part| constant = constant.const_get(part) end constant end geocoder-1.5.1/lib/tasks/maxmind.rake0000644000004100000410000000364213503210114017524 0ustar www-datawww-datarequire 'maxmind_database' namespace :geocoder do namespace :maxmind do namespace :geolite do desc "Download and load/refresh MaxMind GeoLite City data" task load: [:download, :extract, :insert] desc "Download MaxMind GeoLite City data" task :download do p = MaxmindTask.check_for_package! MaxmindTask.download!(p, dir: ENV['DIR'] || "tmp/") end desc "Extract (unzip) MaxMind GeoLite City data" task :extract do p = MaxmindTask.check_for_package! MaxmindTask.extract!(p, dir: ENV['DIR'] || "tmp/") end desc "Load/refresh MaxMind GeoLite City data" task insert: [:environment] do p = MaxmindTask.check_for_package! MaxmindTask.insert!(p, dir: ENV['DIR'] || "tmp/") end end end end module MaxmindTask extend self def check_for_package! if %w[city country].include?(p = ENV['PACKAGE']) return p else puts "Please specify PACKAGE=city or PACKAGE=country" exit end end def download!(package, options = {}) p = "geolite_#{package}_csv".intern Geocoder::MaxmindDatabase.download(p, options[:dir]) end def extract!(package, options = {}) begin require 'zip' rescue LoadError puts "Please install gem: rubyzip (>= 1.0.0)" exit end require 'fileutils' p = "geolite_#{package}_csv".intern archive_filename = Geocoder::MaxmindDatabase.archive_filename(p) Zip::File.open(File.join(options[:dir], archive_filename)).each do |entry| filepath = File.join(options[:dir], entry.name) if File.exist? filepath warn "File already exists (#{entry.name}), skipping" else FileUtils.mkdir_p(File.dirname(filepath)) entry.extract(filepath) end end end def insert!(package, options = {}) p = "geolite_#{package}_csv".intern Geocoder::MaxmindDatabase.insert(p, options[:dir]) end end geocoder-1.5.1/lib/geocoder/0000755000004100000410000000000013503210114015660 5ustar www-datawww-datageocoder-1.5.1/lib/geocoder/ip_address.rb0000644000004100000410000000104013503210114020315 0ustar www-datawww-datarequire 'resolv' module Geocoder class IpAddress < String PRIVATE_IPS = [ '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', ].map { |ip| IPAddr.new(ip) }.freeze def internal? loopback? || private? end def loopback? valid? and !!(self == "0.0.0.0" or self.match(/\A127\./) or self == "::1") end def private? valid? && PRIVATE_IPS.any? { |ip| ip.include?(self) } end def valid? !!((self =~ Resolv::IPv4::Regex) || (self =~ Resolv::IPv6::Regex)) end end end geocoder-1.5.1/lib/geocoder/version.rb0000644000004100000410000000005013503210114017665 0ustar www-datawww-datamodule Geocoder VERSION = "1.5.1" end geocoder-1.5.1/lib/geocoder/cache.rb0000644000004100000410000000400713503210114017251 0ustar www-datawww-datamodule Geocoder class Cache def initialize(store, prefix) @store = store @prefix = prefix end ## # Read from the Cache. # def [](url) interpret case when store.respond_to?(:[]) store[key_for(url)] when store.respond_to?(:get) store.get key_for(url) when store.respond_to?(:read) store.read key_for(url) end end ## # Write to the Cache. # def []=(url, value) case when store.respond_to?(:[]=) store[key_for(url)] = value when store.respond_to?(:set) store.set key_for(url), value when store.respond_to?(:write) store.write key_for(url), value end end ## # Delete cache entry for given URL, # or pass :all to clear all URLs. # def expire(url) if url == :all if store.respond_to?(:keys) urls.each{ |u| expire(u) } else raise(NoMethodError, "The Geocoder cache store must implement `#keys` for `expire(:all)` to work") end else expire_single_url(url) end end private # ---------------------------------------------------------------- def prefix; @prefix; end def store; @store; end ## # Cache key for a given URL. # def key_for(url) [prefix, url].join end ## # Array of keys with the currently configured prefix # that have non-nil values. # def keys store.keys.select{ |k| k.match(/^#{prefix}/) and self[k] } end ## # Array of cached URLs. # def urls keys.map{ |k| k[/^#{prefix}(.*)/, 1] } end ## # Clean up value before returning. Namely, convert empty string to nil. # (Some key/value stores return empty string instead of nil.) # def interpret(value) value == "" ? nil : value end def expire_single_url(url) key = key_for(url) store.respond_to?(:del) ? store.del(key) : store.delete(key) end end end geocoder-1.5.1/lib/geocoder/query.rb0000644000004100000410000000540313503210114017354 0ustar www-datawww-datamodule Geocoder class Query attr_accessor :text, :options def initialize(text, options = {}) self.text = text self.options = options end def execute lookup.search(text, options) end def to_s text end def sanitized_text if coordinates? if text.is_a?(Array) text.join(',') else text.split(/\s*,\s*/).join(',') end else text end end ## # Get a Lookup object (which communicates with the remote geocoding API) # appropriate to the Query text. # def lookup if !options[:street_address] and (options[:ip_address] or ip_address?) name = options[:ip_lookup] || Configuration.ip_lookup || Geocoder::Lookup.ip_services.first else name = options[:lookup] || Configuration.lookup || Geocoder::Lookup.street_services.first end Lookup.get(name) end def url lookup.query_url(self) end ## # Is the Query blank? (ie, should we not bother searching?) # A query is considered blank if its text is nil or empty string AND # no URL parameters are specified. # def blank? !params_given? and ( (text.is_a?(Array) and text.compact.size < 2) or text.to_s.match(/\A\s*\z/) ) end ## # Does the Query text look like an IP address? # # Does not check for actual validity, just the appearance of four # dot-delimited numbers. # def ip_address? IpAddress.new(text).valid? rescue false end ## # Is the Query text a loopback or private IP address? # def internal_ip_address? ip_address? && IpAddress.new(text).internal? end ## # Is the Query text a loopback IP address? # def loopback_ip_address? ip_address? && IpAddress.new(text).loopback? end ## # Is the Query text a private IP address? # def private_ip_address? ip_address? && IpAddress.new(text).private? end ## # Does the given string look like latitude/longitude coordinates? # def coordinates? text.is_a?(Array) or ( text.is_a?(String) and !!text.to_s.match(/\A-?[0-9\.]+, *-?[0-9\.]+\z/) ) end ## # Return the latitude/longitude coordinates specified in the query, # or nil if none. # def coordinates sanitized_text.split(',') if coordinates? end ## # Should reverse geocoding be performed for this query? # def reverse_geocode? coordinates? end def language options[:language] end private # ---------------------------------------------------------------- def params_given? !!(options[:params].is_a?(Hash) and options[:params].keys.size > 0) end end end geocoder-1.5.1/lib/geocoder/sql.rb0000644000004100000410000001067113503210114017011 0ustar www-datawww-datamodule Geocoder module Sql extend self ## # Distance calculation for use with a database that supports POWER(), # SQRT(), PI(), and trigonometric functions SIN(), COS(), ASIN(), # ATAN2(). # # Based on the excellent tutorial at: # http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL # def full_distance(latitude, longitude, lat_attr, lon_attr, options = {}) units = options[:units] || Geocoder.config.units earth = Geocoder::Calculations.earth_radius(units) "#{earth} * 2 * ASIN(SQRT(" + "POWER(SIN((#{latitude.to_f} - #{lat_attr}) * PI() / 180 / 2), 2) + " + "COS(#{latitude.to_f} * PI() / 180) * COS(#{lat_attr} * PI() / 180) * " + "POWER(SIN((#{longitude.to_f} - #{lon_attr}) * PI() / 180 / 2), 2)" + "))" end ## # Distance calculation for use with a database without trigonometric # functions, like SQLite. Approach is to find objects within a square # rather than a circle, so results are very approximate (will include # objects outside the given radius). # # Distance and bearing calculations are *extremely inaccurate*. To be # clear: this only exists to provide interface consistency. Results # are not intended for use in production! # def approx_distance(latitude, longitude, lat_attr, lon_attr, options = {}) units = options[:units] || Geocoder.config.units dx = Geocoder::Calculations.longitude_degree_distance(30, units) dy = Geocoder::Calculations.latitude_degree_distance(units) # sin of 45 degrees = average x or y component of vector factor = Math.sin(Math::PI / 4) "(#{dy} * ABS(#{lat_attr} - #{latitude.to_f}) * #{factor}) + " + "(#{dx} * ABS(#{lon_attr} - #{longitude.to_f}) * #{factor})" end def within_bounding_box(sw_lat, sw_lng, ne_lat, ne_lng, lat_attr, lon_attr) spans = "#{lat_attr} BETWEEN #{sw_lat} AND #{ne_lat} AND " # handle box that spans 180 longitude if sw_lng.to_f > ne_lng.to_f spans + "(#{lon_attr} BETWEEN #{sw_lng} AND 180 OR " + "#{lon_attr} BETWEEN -180 AND #{ne_lng})" else spans + "#{lon_attr} BETWEEN #{sw_lng} AND #{ne_lng}" end end ## # Fairly accurate bearing calculation. Takes a latitude, longitude, # and an options hash which must include a :bearing value # (:linear or :spherical). # # For use with a database that supports MOD() and trigonometric functions # SIN(), COS(), ASIN(), ATAN2(). # # Based on: # http://www.beginningspatial.com/calculating_bearing_one_point_another # def full_bearing(latitude, longitude, lat_attr, lon_attr, options = {}) degrees_per_radian = Geocoder::Calculations::DEGREES_PER_RADIAN case options[:bearing] || Geocoder.config.distances when :linear "MOD(CAST(" + "(ATAN2( " + "((#{lon_attr} - #{longitude.to_f}) / #{degrees_per_radian}), " + "((#{lat_attr} - #{latitude.to_f}) / #{degrees_per_radian})" + ") * #{degrees_per_radian}) + 360 " + "AS decimal), 360)" when :spherical "MOD(CAST(" + "(ATAN2( " + "SIN( (#{lon_attr} - #{longitude.to_f}) / #{degrees_per_radian} ) * " + "COS( (#{lat_attr}) / #{degrees_per_radian} ), (" + "COS( (#{latitude.to_f}) / #{degrees_per_radian} ) * SIN( (#{lat_attr}) / #{degrees_per_radian})" + ") - (" + "SIN( (#{latitude.to_f}) / #{degrees_per_radian}) * COS((#{lat_attr}) / #{degrees_per_radian}) * " + "COS( (#{lon_attr} - #{longitude.to_f}) / #{degrees_per_radian})" + ")" + ") * #{degrees_per_radian}) + 360 " + "AS decimal), 360)" end end ## # Totally lame bearing calculation. Basically useless except that it # returns *something* in databases without trig functions. # def approx_bearing(latitude, longitude, lat_attr, lon_attr, options = {}) "CASE " + "WHEN (#{lat_attr} >= #{latitude.to_f} AND " + "#{lon_attr} >= #{longitude.to_f}) THEN 45.0 " + "WHEN (#{lat_attr} < #{latitude.to_f} AND " + "#{lon_attr} >= #{longitude.to_f}) THEN 135.0 " + "WHEN (#{lat_attr} < #{latitude.to_f} AND " + "#{lon_attr} < #{longitude.to_f}) THEN 225.0 " + "WHEN (#{lat_attr} >= #{latitude.to_f} AND " + "#{lon_attr} < #{longitude.to_f}) THEN 315.0 " + "END" end end end geocoder-1.5.1/lib/geocoder/lookup.rb0000644000004100000410000000506513503210114017524 0ustar www-datawww-datarequire "geocoder/lookups/test" module Geocoder module Lookup extend self ## # Array of valid Lookup service names. # def all_services street_services + ip_services end ## # Array of valid Lookup service names, excluding :test. # def all_services_except_test all_services - [:test] end ## # All street address lookup services, default first. # def street_services @street_services ||= [ :location_iq, :dstk, :esri, :google, :google_premier, :google_places_details, :google_places_search, :bing, :geocoder_ca, :geocoder_us, :yandex, :nominatim, :mapbox, :mapquest, :opencagedata, :pelias, :pickpoint, :here, :baidu, :tencent, :geocodio, :smarty_streets, :postcode_anywhere_uk, :postcodes_io, :geoportail_lu, :ban_data_gouv_fr, :test, :latlon, :amap ] end ## # All IP address lookup services, default first. # def ip_services @ip_services ||= [ :baidu_ip, :freegeoip, :geoip2, :maxmind, :maxmind_local, :telize, :pointpin, :maxmind_geoip2, :ipinfo_io, :ipapi_com, :ipdata_co, :db_ip_com, :ipstack, :ip2location ] end attr_writer :street_services, :ip_services ## # Retrieve a Lookup object from the store. # Use this instead of Geocoder::Lookup::X.new to get an # already-configured Lookup object. # def get(name) @services = {} unless defined?(@services) @services[name] = spawn(name) unless @services.include?(name) @services[name] end private # ----------------------------------------------------------------- ## # Spawn a Lookup of the given name. # def spawn(name) if all_services.include?(name) name = name.to_s require "geocoder/lookups/#{name}" Geocoder::Lookup.const_get(classify_name(name)).new else valids = all_services.map(&:inspect).join(", ") raise ConfigurationError, "Please specify a valid lookup for Geocoder " + "(#{name.inspect} is not one of: #{valids})." end end ## # Convert an "underscore" version of a name into a "class" version. # def classify_name(filename) filename.to_s.split("_").map{ |i| i[0...1].upcase + i[1..-1] }.join end end end geocoder-1.5.1/lib/geocoder/exceptions.rb0000644000004100000410000000102513503210114020364 0ustar www-datawww-datarequire 'timeout' module Geocoder class Error < StandardError end class ConfigurationError < Error end class OverQueryLimitError < Error end class ResponseParseError < Error attr_reader :response def initialize(response) @response = response end end class RequestDenied < Error end class InvalidRequest < Error end class InvalidApiKey < Error end class ServiceUnavailable < Error end class LookupTimeout < ::Timeout::Error end class NetworkError < Error end end geocoder-1.5.1/lib/geocoder/railtie.rb0000644000004100000410000000114013503210114017632 0ustar www-datawww-datarequire 'geocoder/models/active_record' module Geocoder if defined? Rails::Railtie require 'rails' class Railtie < Rails::Railtie initializer 'geocoder.insert_into_active_record', before: :load_config_initializers do ActiveSupport.on_load :active_record do Geocoder::Railtie.insert end end rake_tasks do load "tasks/geocoder.rake" load "tasks/maxmind.rake" end end end class Railtie def self.insert if defined?(::ActiveRecord) ::ActiveRecord::Base.extend(Model::ActiveRecord) end end end end geocoder-1.5.1/lib/geocoder/stores/0000755000004100000410000000000013503210114017177 5ustar www-datawww-datageocoder-1.5.1/lib/geocoder/stores/active_record.rb0000644000004100000410000003007713503210114022344 0ustar www-datawww-data# -*- coding: utf-8 -*- require 'geocoder/sql' require 'geocoder/stores/base' ## # Add geocoding functionality to any ActiveRecord object. # module Geocoder::Store module ActiveRecord include Base ## # Implementation of 'included' hook method. # def self.included(base) base.extend ClassMethods base.class_eval do # scope: geocoded objects scope :geocoded, lambda { where("#{table_name}.#{geocoder_options[:latitude]} IS NOT NULL " + "AND #{table_name}.#{geocoder_options[:longitude]} IS NOT NULL") } # scope: not-geocoded objects scope :not_geocoded, lambda { where("#{table_name}.#{geocoder_options[:latitude]} IS NULL " + "OR #{table_name}.#{geocoder_options[:longitude]} IS NULL") } # scope: not-reverse geocoded objects scope :not_reverse_geocoded, lambda { where("#{table_name}.#{geocoder_options[:fetched_address]} IS NULL") } ## # Find all objects within a radius of the given location. # Location may be either a string to geocode or an array of # coordinates ([lat,lon]). Also takes an options hash # (see Geocoder::Store::ActiveRecord::ClassMethods.near_scope_options # for details). # scope :near, lambda{ |location, *args| latitude, longitude = Geocoder::Calculations.extract_coordinates(location) if Geocoder::Calculations.coordinates_present?(latitude, longitude) options = near_scope_options(latitude, longitude, *args) select(options[:select]).where(options[:conditions]). order(options[:order]) else # If no lat/lon given we don't want any results, but we still # need distance and bearing columns so you can add, for example: # .order("distance") select(select_clause(nil, null_value, null_value)).where(false_condition) end } ## # Find all objects within the area of a given bounding box. # Bounds must be an array of locations specifying the southwest # corner followed by the northeast corner of the box # ([[sw_lat, sw_lon], [ne_lat, ne_lon]]). # scope :within_bounding_box, lambda{ |*bounds| sw_lat, sw_lng, ne_lat, ne_lng = bounds.flatten if bounds if sw_lat && sw_lng && ne_lat && ne_lng where(Geocoder::Sql.within_bounding_box( sw_lat, sw_lng, ne_lat, ne_lng, full_column_name(geocoder_options[:latitude]), full_column_name(geocoder_options[:longitude]) )) else select(select_clause(nil, null_value, null_value)).where(false_condition) end } end end ## # Methods which will be class methods of the including class. # module ClassMethods def distance_from_sql(location, *args) latitude, longitude = Geocoder::Calculations.extract_coordinates(location) if Geocoder::Calculations.coordinates_present?(latitude, longitude) distance_sql(latitude, longitude, *args) end end ## # Get options hash suitable for passing to ActiveRecord.find to get # records within a radius (in kilometers) of the given point. # Options hash may include: # # * +:units+ - :mi or :km; to be used. # for interpreting radius as well as the +distance+ attribute which # is added to each found nearby object. # Use Geocoder.configure[:units] to configure default units. # * +:bearing+ - :linear or :spherical. # the method to be used for calculating the bearing (direction) # between the given point and each found nearby point; # set to false for no bearing calculation. Use # Geocoder.configure[:distances] to configure default calculation method. # * +:select+ - string with the SELECT SQL fragment (e.g. “id, name”) # * +:select_distance+ - whether to include the distance alias in the # SELECT SQL fragment (e.g. AS distance) # * +:select_bearing+ - like +:select_distance+ but for bearing. # * +:order+ - column(s) for ORDER BY SQL clause; default is distance; # set to false or nil to omit the ORDER BY clause # * +:exclude+ - an object to exclude (used by the +nearbys+ method) # * +:distance_column+ - used to set the column name of the calculated distance. # * +:bearing_column+ - used to set the column name of the calculated bearing. # * +:min_radius+ - the value to use as the minimum radius. # ignored if database is sqlite. # default is 0.0 # def near_scope_options(latitude, longitude, radius = 20, options = {}) if options[:units] options[:units] = options[:units].to_sym end latitude_attribute = options[:latitude] || geocoder_options[:latitude] longitude_attribute = options[:longitude] || geocoder_options[:longitude] options[:units] ||= (geocoder_options[:units] || Geocoder.config.units) select_distance = options.fetch(:select_distance) { true } options[:order] = "" if !select_distance && !options.include?(:order) select_bearing = options.fetch(:select_bearing) { true } bearing = bearing_sql(latitude, longitude, options) distance = distance_sql(latitude, longitude, options) distance_column = options.fetch(:distance_column) { 'distance' } bearing_column = options.fetch(:bearing_column) { 'bearing' } # If radius is a DB column name, bounding box should include # all rows within the maximum radius appearing in that column. # Note: performance is dependent on variability of radii. bb_radius = radius.is_a?(Symbol) ? maximum(radius) : radius b = Geocoder::Calculations.bounding_box([latitude, longitude], bb_radius, options) args = b + [ full_column_name(latitude_attribute), full_column_name(longitude_attribute) ] bounding_box_conditions = Geocoder::Sql.within_bounding_box(*args) if using_unextended_sqlite? conditions = bounding_box_conditions else min_radius = options.fetch(:min_radius, 0).to_f # if radius is a DB column name, # find rows between min_radius and value in column if radius.is_a?(Symbol) c = "BETWEEN ? AND #{radius}" a = [min_radius] else c = "BETWEEN ? AND ?" a = [min_radius, radius] end conditions = [bounding_box_conditions + " AND (#{distance}) " + c] + a end { :select => select_clause(options[:select], select_distance ? distance : nil, select_bearing ? bearing : nil, distance_column, bearing_column), :conditions => add_exclude_condition(conditions, options[:exclude]), :order => options.include?(:order) ? options[:order] : "#{distance_column} ASC" } end ## # SQL for calculating distance based on the current database's # capabilities (trig functions?). # def distance_sql(latitude, longitude, options = {}) method_prefix = using_unextended_sqlite? ? "approx" : "full" Geocoder::Sql.send( method_prefix + "_distance", latitude, longitude, full_column_name(options[:latitude] || geocoder_options[:latitude]), full_column_name(options[:longitude]|| geocoder_options[:longitude]), options ) end ## # SQL for calculating bearing based on the current database's # capabilities (trig functions?). # def bearing_sql(latitude, longitude, options = {}) if !options.include?(:bearing) options[:bearing] = Geocoder.config.distances end if options[:bearing] method_prefix = using_unextended_sqlite? ? "approx" : "full" Geocoder::Sql.send( method_prefix + "_bearing", latitude, longitude, full_column_name(options[:latitude] || geocoder_options[:latitude]), full_column_name(options[:longitude]|| geocoder_options[:longitude]), options ) end end ## # Generate the SELECT clause. # def select_clause(columns, distance = nil, bearing = nil, distance_column = 'distance', bearing_column = 'bearing') if columns == :id_only return full_column_name(primary_key) elsif columns == :geo_only clause = "" else clause = (columns || full_column_name("*")) end if distance clause += ", " unless clause.empty? clause += "#{distance} AS #{distance_column}" end if bearing clause += ", " unless clause.empty? clause += "#{bearing} AS #{bearing_column}" end clause end ## # Adds a condition to exclude a given object by ID. # Expects conditions as an array or string. Returns array. # def add_exclude_condition(conditions, exclude) conditions = [conditions] if conditions.is_a?(String) if exclude conditions[0] << " AND #{full_column_name(primary_key)} != ?" conditions << exclude.id end conditions end def using_unextended_sqlite? using_sqlite? && !using_sqlite_with_extensions? end def using_sqlite? !!connection.adapter_name.match(/sqlite/i) end def using_sqlite_with_extensions? connection.adapter_name.match(/sqlite/i) && defined?(::SqliteExt) && %W(MOD POWER SQRT PI SIN COS ASIN ATAN2).all?{ |fn_name| connection.raw_connection.function_created?(fn_name) } end def using_postgres? connection.adapter_name.match(/postgres/i) end ## # Use OID type when running in PosgreSQL # def null_value using_postgres? ? 'NULL::text' : 'NULL' end ## # Value which can be passed to where() to produce no results. # def false_condition using_unextended_sqlite? ? 0 : "false" end ## # Prepend table name if column name doesn't already contain one. # def full_column_name(column) column = column.to_s column.include?(".") ? column : [table_name, column].join(".") end end ## # Get nearby geocoded objects. # Takes the same options hash as the near class method (scope). # Returns nil if the object is not geocoded. # def nearbys(radius = 20, options = {}) return nil unless geocoded? options.merge!(:exclude => self) unless send(self.class.primary_key).nil? self.class.near(self, radius, options) end ## # Look up coordinates and assign to +latitude+ and +longitude+ attributes # (or other as specified in +geocoded_by+). Returns coordinates (array). # def geocode do_lookup(false) do |o,rs| if r = rs.first unless r.latitude.nil? or r.longitude.nil? o.__send__ "#{self.class.geocoder_options[:latitude]}=", r.latitude o.__send__ "#{self.class.geocoder_options[:longitude]}=", r.longitude end r.coordinates end end end alias_method :fetch_coordinates, :geocode ## # Look up address and assign to +address+ attribute (or other as specified # in +reverse_geocoded_by+). Returns address (string). # def reverse_geocode do_lookup(true) do |o,rs| if r = rs.first unless r.address.nil? o.__send__ "#{self.class.geocoder_options[:fetched_address]}=", r.address end r.address end end end alias_method :fetch_address, :reverse_geocode end end geocoder-1.5.1/lib/geocoder/stores/mongo_base.rb0000644000004100000410000000276513503210114021647 0ustar www-datawww-datamodule Geocoder::Store module MongoBase def self.included_by_model(base) base.class_eval do scope :geocoded, lambda { where(geocoder_options[:coordinates].ne => nil) } scope :not_geocoded, lambda { where(geocoder_options[:coordinates] => nil) } end end ## # Coordinates [lat,lon] of the object. # This method always returns coordinates in lat,lon order, # even though internally they are stored in the opposite order. # def to_coordinates coords = send(self.class.geocoder_options[:coordinates]) coords.is_a?(Array) ? coords.reverse : [] end ## # Look up coordinates and assign to +latitude+ and +longitude+ attributes # (or other as specified in +geocoded_by+). Returns coordinates (array). # def geocode do_lookup(false) do |o,rs| if r = rs.first unless r.coordinates.nil? o.__send__ "#{self.class.geocoder_options[:coordinates]}=", r.coordinates.reverse end r.coordinates end end end ## # Look up address and assign to +address+ attribute (or other as specified # in +reverse_geocoded_by+). Returns address (string). # def reverse_geocode do_lookup(true) do |o,rs| if r = rs.first unless r.address.nil? o.__send__ "#{self.class.geocoder_options[:fetched_address]}=", r.address end r.address end end end end end geocoder-1.5.1/lib/geocoder/stores/mongoid.rb0000644000004100000410000000035313503210114021161 0ustar www-datawww-datarequire 'geocoder/stores/base' require 'geocoder/stores/mongo_base' module Geocoder::Store module Mongoid include Base include MongoBase def self.included(base) MongoBase.included_by_model(base) end end end geocoder-1.5.1/lib/geocoder/stores/base.rb0000644000004100000410000000720113503210114020436 0ustar www-datawww-datamodule Geocoder module Store module Base ## # Is this object geocoded? (Does it have latitude and longitude?) # def geocoded? to_coordinates.compact.size == 2 end ## # Coordinates [lat,lon] of the object. # def to_coordinates [:latitude, :longitude].map{ |i| send self.class.geocoder_options[i] } end ## # Calculate the distance from the object to an arbitrary point. # See Geocoder::Calculations.distance_between for ways of specifying # the point. Also takes a symbol specifying the units # (:mi or :km; can be specified in Geocoder configuration). # def distance_to(point, units = nil) units ||= self.class.geocoder_options[:units] return nil unless geocoded? Geocoder::Calculations.distance_between( to_coordinates, point, :units => units) end alias_method :distance_from, :distance_to ## # Calculate the bearing from the object to another point. # See Geocoder::Calculations.distance_between for # ways of specifying the point. # def bearing_to(point, options = {}) options[:method] ||= self.class.geocoder_options[:method] return nil unless geocoded? Geocoder::Calculations.bearing_between( to_coordinates, point, options) end ## # Calculate the bearing from another point to the object. # See Geocoder::Calculations.distance_between for # ways of specifying the point. # def bearing_from(point, options = {}) options[:method] ||= self.class.geocoder_options[:method] return nil unless geocoded? Geocoder::Calculations.bearing_between( point, to_coordinates, options) end ## # Look up coordinates and assign to +latitude+ and +longitude+ attributes # (or other as specified in +geocoded_by+). Returns coordinates (array). # def geocode fail end ## # Look up address and assign to +address+ attribute (or other as specified # in +reverse_geocoded_by+). Returns address (string). # def reverse_geocode fail end private # -------------------------------------------------------------- ## # Look up geographic data based on object attributes (configured in # geocoded_by or reverse_geocoded_by) and handle the results with the # block (given to geocoded_by or reverse_geocoded_by). The block is # given two-arguments: the object being geocoded and an array of # Geocoder::Result objects). # def do_lookup(reverse = false) options = self.class.geocoder_options if reverse and options[:reverse_geocode] query = to_coordinates elsif !reverse and options[:geocode] query = send(options[:user_address]) else return end query_options = [:lookup, :ip_lookup, :language, :params].inject({}) do |hash, key| if options.has_key?(key) val = options[key] hash[key] = val.respond_to?(:call) ? val.call(self) : val end hash end results = Geocoder.search(query, query_options) # execute custom block, if specified in configuration block_key = reverse ? :reverse_block : :geocode_block if custom_block = options[block_key] custom_block.call(self, results) # else execute block passed directly to this method, # which generally performs the "auto-assigns" elsif block_given? yield(self, results) end end end end end geocoder-1.5.1/lib/geocoder/stores/mongo_mapper.rb0000644000004100000410000000035713503210114022214 0ustar www-datawww-datarequire 'geocoder/stores/base' require 'geocoder/stores/mongo_base' module Geocoder::Store module MongoMapper include Base include MongoBase def self.included(base) MongoBase.included_by_model(base) end end end geocoder-1.5.1/lib/geocoder/calculations.rb0000644000004100000410000003306113503210114020671 0ustar www-datawww-datamodule Geocoder module Calculations extend self ## # Compass point names, listed clockwise starting at North. # # If you want bearings named using more, fewer, or different points # override Geocoder::Calculations.COMPASS_POINTS with your own array. # COMPASS_POINTS = %w[N NE E SE S SW W NW] ## # Conversion factor: multiply by kilometers to get miles. # KM_IN_MI = 0.621371192 ## # Conversion factor: multiply by nautical miles to get miles. # KM_IN_NM = 0.539957 ## # Conversion factor: multiply by radians to get degrees. # DEGREES_PER_RADIAN = 57.2957795 ## # Radius of the Earth, in kilometers. # Value taken from: http://en.wikipedia.org/wiki/Earth_radius # EARTH_RADII = {km: 6371.0} EARTH_RADII[:mi] = EARTH_RADII[:km] * KM_IN_MI EARTH_RADII[:nm] = EARTH_RADII[:km] * KM_IN_NM EARTH_RADIUS = EARTH_RADII[:km] # TODO: deprecate this constant (use `EARTH_RADII[:km]`) # Not a number constant NAN = defined?(::Float::NAN) ? ::Float::NAN : 0 / 0.0 ## # Returns true if all given arguments are valid latitude/longitude values. # def coordinates_present?(*args) args.each do |a| # note that Float::NAN != Float::NAN # still, this could probably be improved: return false if (!a.is_a?(Numeric) or a.to_s == "NaN") end true end ## # Distance spanned by one degree of latitude in the given units. # def latitude_degree_distance(units = nil) 2 * Math::PI * earth_radius(units) / 360 end ## # Distance spanned by one degree of longitude at the given latitude. # This ranges from around 69 miles at the equator to zero at the poles. # def longitude_degree_distance(latitude, units = nil) latitude_degree_distance(units) * Math.cos(to_radians(latitude)) end ## # Distance between two points on Earth (Haversine formula). # Takes two points and an options hash. # The points are given in the same way that points are given to all # Geocoder methods that accept points as arguments. They can be: # # * an array of coordinates ([lat,lon]) # * a geocodable address (string) # * a geocoded object (one which implements a +to_coordinates+ method # which returns a [lat,lon] array # # The options hash supports: # # * :units - :mi or :km # Use Geocoder.configure(:units => ...) to configure default units. # def distance_between(point1, point2, options = {}) # convert to coordinate arrays point1 = extract_coordinates(point1) point2 = extract_coordinates(point2) # convert degrees to radians point1 = to_radians(point1) point2 = to_radians(point2) # compute deltas dlat = point2[0] - point1[0] dlon = point2[1] - point1[1] a = (Math.sin(dlat / 2))**2 + Math.cos(point1[0]) * (Math.sin(dlon / 2))**2 * Math.cos(point2[0]) c = 2 * Math.atan2( Math.sqrt(a), Math.sqrt(1-a)) c * earth_radius(options[:units]) end ## # Bearing between two points on Earth. # Returns a number of degrees from due north (clockwise). # # See Geocoder::Calculations.distance_between for # ways of specifying the points. Also accepts an options hash: # # * :method - :linear or :spherical; # the spherical method is "correct" in that it returns the shortest path # (one along a great circle) but the linear method is less confusing # (returns due east or west when given two points with the same latitude). # Use Geocoder.configure(:distances => ...) to configure calculation method. # # Based on: http://www.movable-type.co.uk/scripts/latlong.html # def bearing_between(point1, point2, options = {}) # set default options options[:method] ||= Geocoder.config.distances options[:method] = :linear unless options[:method] == :spherical # convert to coordinate arrays point1 = extract_coordinates(point1) point2 = extract_coordinates(point2) # convert degrees to radians point1 = to_radians(point1) point2 = to_radians(point2) # compute deltas dlat = point2[0] - point1[0] dlon = point2[1] - point1[1] case options[:method] when :linear y = dlon x = dlat when :spherical y = Math.sin(dlon) * Math.cos(point2[0]) x = Math.cos(point1[0]) * Math.sin(point2[0]) - Math.sin(point1[0]) * Math.cos(point2[0]) * Math.cos(dlon) end bearing = Math.atan2(x,y) # Answer is in radians counterclockwise from due east. # Convert to degrees clockwise from due north: (90 - to_degrees(bearing) + 360) % 360 end ## # Translate a bearing (float) into a compass direction (string, eg "North"). # def compass_point(bearing, points = COMPASS_POINTS) seg_size = 360.0 / points.size points[((bearing + (seg_size / 2)) % 360) / seg_size] end ## # Compute the geographic center (aka geographic midpoint, center of # gravity) for an array of geocoded objects and/or [lat,lon] arrays # (can be mixed). Any objects missing coordinates are ignored. Follows # the procedure documented at http://www.geomidpoint.com/calculation.html. # def geographic_center(points) # convert objects to [lat,lon] arrays and convert degrees to radians coords = points.map{ |p| to_radians(extract_coordinates(p)) } # convert to Cartesian coordinates x = []; y = []; z = [] coords.each do |p| x << Math.cos(p[0]) * Math.cos(p[1]) y << Math.cos(p[0]) * Math.sin(p[1]) z << Math.sin(p[0]) end # compute average coordinate values xa, ya, za = [x,y,z].map do |c| c.inject(0){ |tot,i| tot += i } / c.size.to_f end # convert back to latitude/longitude lon = Math.atan2(ya, xa) hyp = Math.sqrt(xa**2 + ya**2) lat = Math.atan2(za, hyp) # return answer in degrees to_degrees [lat, lon] end ## # Returns coordinates of the southwest and northeast corners of a box # with the given point at its center. The radius is the shortest distance # from the center point to any side of the box (the length of each side # is twice the radius). # # This is useful for finding corner points of a map viewport, or for # roughly limiting the possible solutions in a geo-spatial search # (ActiveRecord queries use it thusly). # # See Geocoder::Calculations.distance_between for # ways of specifying the point. Also accepts an options hash: # # * :units - :mi or :km. # Use Geocoder.configure(:units => ...) to configure default units. # def bounding_box(point, radius, options = {}) lat,lon = extract_coordinates(point) radius = radius.to_f [ lat - (radius / latitude_degree_distance(options[:units])), lon - (radius / longitude_degree_distance(lat, options[:units])), lat + (radius / latitude_degree_distance(options[:units])), lon + (radius / longitude_degree_distance(lat, options[:units])) ] end ## # Random point within a circle of provided radius centered # around the provided point # Takes one point, one radius, and an options hash. # The points are given in the same way that points are given to all # Geocoder methods that accept points as arguments. They can be: # # * an array of coordinates ([lat,lon]) # * a geocodable address (string) # * a geocoded object (one which implements a +to_coordinates+ method # which returns a [lat,lon] array # # The options hash supports: # # * :units - :mi or :km # Use Geocoder.configure(:units => ...) to configure default units. # * :seed - The seed for the random number generator def random_point_near(center, radius, options = {}) random = Random.new(options[:seed] || Random.new_seed) # convert to coordinate arrays center = extract_coordinates(center) earth_circumference = 2 * Math::PI * earth_radius(options[:units]) max_degree_delta = 360.0 * (radius / earth_circumference) # random bearing in radians theta = 2 * Math::PI * random.rand # random radius, use the square root to ensure a uniform # distribution of points over the circle r = Math.sqrt(random.rand) * max_degree_delta delta_lat, delta_long = [r * Math.cos(theta), r * Math.sin(theta)] [center[0] + delta_lat, center[1] + delta_long] end ## # Given a start point, heading (in degrees), and distance, provides # an endpoint. # The starting point is given in the same way that points are given to all # Geocoder methods that accept points as arguments. It can be: # # * an array of coordinates ([lat,lon]) # * a geocodable address (string) # * a geocoded object (one which implements a +to_coordinates+ method # which returns a [lat,lon] array # def endpoint(start, heading, distance, options = {}) radius = earth_radius(options[:units]) start = extract_coordinates(start) # convert degrees to radians start = to_radians(start) lat = start[0] lon = start[1] heading = to_radians(heading) distance = distance.to_f end_lat = Math.asin(Math.sin(lat)*Math.cos(distance/radius) + Math.cos(lat)*Math.sin(distance/radius)*Math.cos(heading)) end_lon = lon+Math.atan2(Math.sin(heading)*Math.sin(distance/radius)*Math.cos(lat), Math.cos(distance/radius)-Math.sin(lat)*Math.sin(end_lat)) to_degrees [end_lat, end_lon] end ## # Convert degrees to radians. # If an array (or multiple arguments) is passed, # converts each value and returns array. # def to_radians(*args) args = args.first if args.first.is_a?(Array) if args.size == 1 args.first * (Math::PI / 180) else args.map{ |i| to_radians(i) } end end ## # Convert radians to degrees. # If an array (or multiple arguments) is passed, # converts each value and returns array. # def to_degrees(*args) args = args.first if args.first.is_a?(Array) if args.size == 1 (args.first * 180.0) / Math::PI else args.map{ |i| to_degrees(i) } end end def distance_to_radians(distance, units = nil) distance.to_f / earth_radius(units) end def radians_to_distance(radians, units = nil) radians * earth_radius(units) end ## # Convert miles to kilometers. # def to_kilometers(mi) Geocoder.log(:warn, "DEPRECATION WARNING: Geocoder::Calculations.to_kilometers is deprecated and will be removed in Geocoder 1.5.0. Please multiply by MI_IN_KM instead.") mi * mi_in_km end ## # Convert kilometers to miles. # def to_miles(km) Geocoder.log(:warn, "DEPRECATION WARNING: Geocoder::Calculations.to_miles is deprecated and will be removed in Geocoder 1.5.0. Please multiply by KM_IN_MI instead.") km * KM_IN_MI end ## # Convert kilometers to nautical miles. # def to_nautical_miles(km) Geocoder.log(:warn, "DEPRECATION WARNING: Geocoder::Calculations.to_nautical_miles is deprecated and will be removed in Geocoder 1.5.0. Please multiply by KM_IN_NM instead.") km * KM_IN_NM end ## # Radius of the Earth in the given units (:mi or :km). # Use Geocoder.configure(:units => ...) to configure default units. # def earth_radius(units = nil) EARTH_RADII[units || Geocoder.config.units] end ## # Conversion factor: km to mi. # def km_in_mi Geocoder.log(:warn, "DEPRECATION WARNING: Geocoder::Calculations.km_in_mi is deprecated and will be removed in Geocoder 1.5.0. Please use the constant KM_IN_MI instead.") KM_IN_MI end ## # Conversion factor: km to nm. # def km_in_nm Geocoder.log(:warn, "DEPRECATION WARNING: Geocoder::Calculations.km_in_nm is deprecated and will be removed in Geocoder 1.5.0. Please use the constant KM_IN_NM instead.") KM_IN_NM end ## # Conversion factor: mi to km. # def mi_in_km Geocoder.log(:warn, "DEPRECATION WARNING: Geocoder::Calculations.mi_in_km is deprecated and will be removed in Geocoder 1.5.0. Please use 1.0 / KM_IN_MI instead.") 1.0 / KM_IN_MI end ## # Conversion factor: nm to km. # def nm_in_km Geocoder.log(:warn, "DEPRECATION WARNING: Geocoder::Calculations.nm_in_km is deprecated and will be removed in Geocoder 1.5.0. Please use 1.0 / KM_IN_NM instead.") 1.0 / KM_IN_NM end ## # Takes an object which is a [lat,lon] array, a geocodable string, # or an object that implements +to_coordinates+ and returns a # [lat,lon] array. Note that if a string is passed this may be a slow- # running method and may return nil. # def extract_coordinates(point) case point when Array if point.size == 2 lat, lon = point if !lat.nil? && lat.respond_to?(:to_f) and !lon.nil? && lon.respond_to?(:to_f) then return [ lat.to_f, lon.to_f ] end end when String point = Geocoder.coordinates(point) and return point else if point.respond_to?(:to_coordinates) if Array === array = point.to_coordinates return extract_coordinates(array) end end end [ NAN, NAN ] end end end geocoder-1.5.1/lib/geocoder/models/0000755000004100000410000000000013503210114017143 5ustar www-datawww-datageocoder-1.5.1/lib/geocoder/models/active_record.rb0000644000004100000410000000313313503210114022301 0ustar www-datawww-datarequire 'geocoder/models/base' module Geocoder module Model module ActiveRecord include Base ## # Set attribute names and include the Geocoder module. # def geocoded_by(address_attr, options = {}, &block) geocoder_init( :geocode => true, :user_address => address_attr, :latitude => options[:latitude] || :latitude, :longitude => options[:longitude] || :longitude, :geocode_block => block, :units => options[:units], :method => options[:method], :lookup => options[:lookup], :language => options[:language], :params => options[:params] ) end ## # Set attribute names and include the Geocoder module. # def reverse_geocoded_by(latitude_attr, longitude_attr, options = {}, &block) geocoder_init( :reverse_geocode => true, :fetched_address => options[:address] || :address, :latitude => latitude_attr, :longitude => longitude_attr, :reverse_block => block, :units => options[:units], :method => options[:method], :lookup => options[:lookup], :language => options[:language], :params => options[:params] ) end private # -------------------------------------------------------------- def geocoder_file_name; "active_record"; end def geocoder_module_name; "ActiveRecord"; end end end end geocoder-1.5.1/lib/geocoder/models/mongo_base.rb0000644000004100000410000000354313503210114021606 0ustar www-datawww-datamodule Geocoder ## # Methods for invoking Geocoder in a model. # module Model module MongoBase ## # Set attribute names and include the Geocoder module. # def geocoded_by(address_attr, options = {}, &block) geocoder_init( :geocode => true, :user_address => address_attr, :coordinates => options[:coordinates] || :coordinates, :geocode_block => block, :units => options[:units], :method => options[:method], :skip_index => options[:skip_index] || false, :lookup => options[:lookup], :language => options[:language] ) end ## # Set attribute names and include the Geocoder module. # def reverse_geocoded_by(coordinates_attr, options = {}, &block) geocoder_init( :reverse_geocode => true, :fetched_address => options[:address] || :address, :coordinates => coordinates_attr, :reverse_block => block, :units => options[:units], :method => options[:method], :skip_index => options[:skip_index] || false, :lookup => options[:lookup], :language => options[:language] ) end private # ---------------------------------------------------------------- def geocoder_init(options) unless geocoder_initialized? @geocoder_options = { } require "geocoder/stores/#{geocoder_file_name}" include Geocoder::Store.const_get(geocoder_module_name) end @geocoder_options.merge! options end def geocoder_initialized? included_modules.include? Geocoder::Store.const_get(geocoder_module_name) rescue NameError false end end end end geocoder-1.5.1/lib/geocoder/models/mongoid.rb0000644000004100000410000000155513503210114021132 0ustar www-datawww-datarequire 'geocoder/models/base' require 'geocoder/models/mongo_base' module Geocoder module Model module Mongoid include Base include MongoBase def self.included(base); base.extend(self); end private # -------------------------------------------------------------- def geocoder_file_name; "mongoid"; end def geocoder_module_name; "Mongoid"; end def geocoder_init(options) super(options) if options[:skip_index] == false # create 2d index if defined?(::Mongoid::VERSION) && ::Mongoid::VERSION >= "3" index({ geocoder_options[:coordinates].to_sym => '2d' }, {:min => -180, :max => 180}) else index [[ geocoder_options[:coordinates], '2d' ]], :min => -180, :max => 180 end end end end end end geocoder-1.5.1/lib/geocoder/models/base.rb0000644000004100000410000000151513503210114020404 0ustar www-datawww-datamodule Geocoder ## # Methods for invoking Geocoder in a model. # module Model module Base def geocoder_options if defined?(@geocoder_options) @geocoder_options elsif superclass.respond_to?(:geocoder_options) superclass.geocoder_options || { } else { } end end def geocoded_by fail end def reverse_geocoded_by fail end private # ---------------------------------------------------------------- def geocoder_init(options) unless defined?(@geocoder_options) @geocoder_options = {} require "geocoder/stores/#{geocoder_file_name}" include Geocoder::Store.const_get(geocoder_module_name) end @geocoder_options.merge! options end end end end geocoder-1.5.1/lib/geocoder/models/mongo_mapper.rb0000644000004100000410000000124213503210114022152 0ustar www-datawww-datarequire 'geocoder/models/base' require 'geocoder/models/mongo_base' module Geocoder module Model module MongoMapper include Base include MongoBase def self.included(base); base.extend(self); end private # -------------------------------------------------------------- def geocoder_file_name; "mongo_mapper"; end def geocoder_module_name; "MongoMapper"; end def geocoder_init(options) super(options) if options[:skip_index] == false ensure_index [[ geocoder_options[:coordinates], Mongo::GEO2D ]], :min => -180, :max => 180 # create 2d index end end end end end geocoder-1.5.1/lib/geocoder/lookups/0000755000004100000410000000000013503210114017354 5ustar www-datawww-datageocoder-1.5.1/lib/geocoder/lookups/opencagedata.rb0000644000004100000410000000360013503210114022313 0ustar www-datawww-datarequire 'geocoder/lookups/base' require 'geocoder/results/opencagedata' module Geocoder::Lookup class Opencagedata < Base def name "OpenCageData" end def required_api_key_parts ["key"] end private # ---------------------------------------------------------------- def base_query_url(query) "#{protocol}://api.opencagedata.com/geocode/v1/json?" end def results(query) return [] unless doc = fetch_data(query) # return doc["results"] messages = doc['status']['message'] case doc['status']['code'] when 400 # Error with input raise_error(Geocoder::InvalidRequest, messages) || Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}") when 403 # Key related error raise_error(Geocoder::InvalidApiKey, messages) || Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}") when 402 # Quata Exceeded raise_error(Geocoder::OverQueryLimitError, messages) || Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}") when 500 # Unknown error raise_error(Geocoder::Error, messages) || Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}") end return doc["results"] end def query_url_params(query) params = { :q => query.sanitized_text, :key => configuration.api_key, :language => (query.language || configuration.language) }.merge(super) [:abbrv, :countrycode, :min_confidence, :no_dedupe, :no_annotations, :no_record, :limit].each do |option| unless (option_value = query.options[option]).nil? params[option] = option_value end end unless (bounds = query.options[:bounds]).nil? params[:bounds] = bounds.map{ |point| "%f,%f" % point }.join(',') end params end end end geocoder-1.5.1/lib/geocoder/lookups/here.rb0000644000004100000410000000357413503210114020635 0ustar www-datawww-datarequire 'geocoder/lookups/base' require 'geocoder/results/here' module Geocoder::Lookup class Here < Base def name "Here" end def required_api_key_parts ["app_id", "app_code"] end private # --------------------------------------------------------------- def base_query_url(query) "#{protocol}://#{if query.reverse_geocode? then 'reverse.' end}geocoder.api.here.com/6.2/#{if query.reverse_geocode? then 'reverse' end}geocode.json?" end def results(query) return [] unless doc = fetch_data(query) return [] unless doc['Response'] && doc['Response']['View'] if r=doc['Response']['View'] return [] if r.nil? || !r.is_a?(Array) || r.empty? return r.first['Result'] end [] end def query_url_here_options(query, reverse_geocode) options = { gen: 9, app_id: api_key, app_code: api_code, language: (query.language || configuration.language) } if reverse_geocode options[:mode] = :retrieveAddresses return options end unless (country = query.options[:country]).nil? options[:country] = country end unless (mapview = query.options[:bounds]).nil? options[:mapview] = mapview.map{ |point| "%f,%f" % point }.join(';') end options end def query_url_params(query) if query.reverse_geocode? super.merge(query_url_here_options(query, true)).merge( prox: query.sanitized_text ) else super.merge(query_url_here_options(query, false)).merge( searchtext: query.sanitized_text ) end end def api_key if (a = configuration.api_key) return a.first if a.is_a?(Array) end end def api_code if (a = configuration.api_key) return a.last if a.is_a?(Array) end end end end geocoder-1.5.1/lib/geocoder/lookups/amap.rb0000644000004100000410000000316013503210114020617 0ustar www-datawww-datarequire 'geocoder/lookups/base' require "geocoder/results/amap" module Geocoder::Lookup class Amap < Base def name "AMap" end def required_api_key_parts ["key"] end def supported_protocols [:http] end private # --------------------------------------------------------------- def base_query_url(query) path = query.reverse_geocode? ? 'regeo' : 'geo' "http://restapi.amap.com/v3/geocode/#{path}?" end def results(query, reverse = false) return [] unless doc = fetch_data(query) case [doc['status'], doc['info']] when ['1', 'OK'] return doc['regeocodes'] unless doc['regeocodes'].blank? return [doc['regeocode']] unless doc['regeocode'].blank? return doc['geocodes'] unless doc['geocodes'].blank? when ['0', 'INVALID_USER_KEY'] raise_error(Geocoder::InvalidApiKey, "invalid api key") || warn("#{self.name} Geocoding API error: invalid api key.") else raise_error(Geocoder::Error, "server error.") || warn("#{self.name} Geocoding API error: server error - [#{doc['info']}]") end return [] end def query_url_params(query) params = { :key => configuration.api_key, :output => "json" } if query.reverse_geocode? params[:location] = revert_coordinates(query.text) params[:extensions] = "all" params[:coordsys] = "gps" else params[:address] = query.sanitized_text end params.merge(super) end def revert_coordinates(text) [text[1],text[0]].join(",") end end end geocoder-1.5.1/lib/geocoder/lookups/ipdata_co.rb0000644000004100000410000000304113503210114021622 0ustar www-datawww-datarequire 'geocoder/lookups/base' require 'geocoder/results/ipdata_co' module Geocoder::Lookup class IpdataCo < Base def name "ipdata.co" end def supported_protocols [:https] end def query_url(query) # Ipdata.co requires that the API key be sent as a query parameter. # It no longer supports API keys sent as headers. "#{protocol}://#{host}/#{query.sanitized_text}?api-key=#{configuration.api_key}" end private # --------------------------------------------------------------- def cache_key(query) query_url(query) end def results(query) # don't look up a loopback or private address, just return the stored result return [reserved_result(query.text)] if query.internal_ip_address? # note: Ipdata.co returns plain text on bad request (doc = fetch_data(query)) ? [doc] : [] end def reserved_result(ip) { "ip" => ip, "city" => "", "region_code" => "", "region_name" => "", "metrocode" => "", "zipcode" => "", "latitude" => "0", "longitude" => "0", "country_name" => "Reserved", "country_code" => "RD" } end def host "api.ipdata.co" end def check_response_for_errors!(response) if response.code.to_i == 403 raise_error(Geocoder::RequestDenied) || Geocoder.log(:warn, "Geocoding API error: 403 API key does not exist") else super(response) end end end end geocoder-1.5.1/lib/geocoder/lookups/geoportail_lu.rb0000644000004100000410000000275313503210114022555 0ustar www-datawww-datarequire 'geocoder/lookups/base' require "geocoder/results/geoportail_lu" module Geocoder module Lookup class GeoportailLu < Base def name "Geoportail.lu" end private # --------------------------------------------------------------- def base_query_url(query) if query.reverse_geocode? reverse_geocode_url_base_path else search_url_base_path end end def search_url_base_path "#{protocol}://api.geoportail.lu/geocoder/search?" end def reverse_geocode_url_base_path "#{protocol}://api.geoportail.lu/geocoder/reverseGeocode?" end def query_url_geoportail_lu_params(query) query.reverse_geocode? ? reverse_geocode_params(query) : search_params(query) end def search_params(query) { queryString: query.sanitized_text } end def reverse_geocode_params(query) lat_lon = query.coordinates { lat: lat_lon.first, lon: lat_lon.last } end def query_url_params(query) query_url_geoportail_lu_params(query).merge(super) end def results(query) return [] unless doc = fetch_data(query) if doc['success'] == true result = doc['results'] else result = [] raise_error(Geocoder::Error) || warn("Geportail.lu Geocoding API error") end result end end end end geocoder-1.5.1/lib/geocoder/lookups/baidu_ip.rb0000644000004100000410000000105313503210114021454 0ustar www-datawww-datarequire 'geocoder/lookups/baidu' require 'geocoder/results/baidu_ip' module Geocoder::Lookup class BaiduIp < Baidu def name "Baidu IP" end private # --------------------------------------------------------------- def base_query_url(query) "#{protocol}://api.map.baidu.com/location/ip?" end def content_key 'content' end def query_url_params(query) { :ip => query.sanitized_text, :ak => configuration.api_key, :coor => "bd09ll" }.merge(super) end end end geocoder-1.5.1/lib/geocoder/lookups/yandex.rb0000644000004100000410000000333313503210114021173 0ustar www-datawww-datarequire 'geocoder/lookups/base' require "geocoder/results/yandex" module Geocoder::Lookup class Yandex < Base def name "Yandex" end def map_link_url(coordinates) "http://maps.yandex.ru/?ll=#{coordinates.reverse.join(',')}" end def supported_protocols [:https] end private # --------------------------------------------------------------- def base_query_url(query) "#{protocol}://geocode-maps.yandex.ru/1.x/?" end def results(query) return [] unless doc = fetch_data(query) if err = doc['error'] if err["status"] == 401 and err["message"] == "invalid key" raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid API key.") else Geocoder.log(:warn, "Yandex Geocoding API error: #{err['status']} (#{err['message']}).") end return [] end if doc = doc['response']['GeoObjectCollection'] meta = doc['metaDataProperty']['GeocoderResponseMetaData'] return meta['found'].to_i > 0 ? doc['featureMember'] : [] else Geocoder.log(:warn, "Yandex Geocoding API error: unexpected response format.") return [] end end def query_url_params(query) if query.reverse_geocode? q = query.coordinates.reverse.join(",") else q = query.sanitized_text end params = { :geocode => q, :format => "json", :plng => "#{query.language || configuration.language}", # supports ru, uk, be :key => configuration.api_key } unless (bounds = query.options[:bounds]).nil? params[:bbox] = bounds.map{ |point| "%f,%f" % point }.join('~') end params.merge(super) end end end geocoder-1.5.1/lib/geocoder/lookups/tencent.rb0000644000004100000410000000304113503210114021337 0ustar www-datawww-datarequire 'geocoder/lookups/base' require "geocoder/results/tencent" module Geocoder::Lookup class Tencent < Base def name "Tencent" end def required_api_key_parts ["key"] end def supported_protocols [:https] end private # --------------------------------------------------------------- def base_query_url(query) "#{protocol}://apis.map.qq.com/ws/geocoder/v1/?" end def content_key 'result' end def results(query, reverse = false) return [] unless doc = fetch_data(query) case doc['status'] when 0 return [doc[content_key]] when 199 raise error(Geocoder::InvalidApiKey, "invalid api key") || Geocoder.log(:warn, "#{name} Geocoding API error: key is not enabled for web service usage.") when 311 raise_error(Geocoder::RequestDenied, "request denied") || Geocoder.log(:warn, "#{name} Geocoding API error: request denied.") when 310, 306 raise_error(Geocoder::InvalidRequest, "invalid request.") || Geocoder.log(:warn, "#{name} Geocoding API error: invalid request.") when 311 raise_error(Geocoder::InvalidApiKey, "invalid api key") || Geocoder.log(:warn, "#{name} Geocoding API error: invalid api key.") end return [] end def query_url_params(query) { (query.reverse_geocode? ? :location : :address) => query.sanitized_text, :key => configuration.api_key, :output => "json" }.merge(super) end end end geocoder-1.5.1/lib/geocoder/lookups/postcodes_io.rb0000644000004100000410000000122713503210114022375 0ustar www-datawww-datarequire 'geocoder/lookups/base' require 'geocoder/results/postcodes_io' module Geocoder::Lookup class PostcodesIo < Base def name 'Postcodes.io' end def query_url(query) "#{protocol}://api.postcodes.io/postcodes/#{query.sanitized_text.gsub(/\s/, '')}" end def supported_protocols [:https] end private # ---------------------------------------------------------------- def cache_key(query) query_url(query) end def results(query) response = fetch_data(query) return [] if response.nil? || response['status'] != 200 || response.empty? [response['result']] end end end geocoder-1.5.1/lib/geocoder/lookups/telize.rb0000644000004100000410000000324613503210114021202 0ustar www-datawww-datarequire 'geocoder/lookups/base' require 'geocoder/results/telize' module Geocoder::Lookup class Telize < Base def name "Telize" end def required_api_key_parts configuration[:host] ? [] : ["key"] end def query_url(query) if configuration[:host] "#{protocol}://#{configuration[:host]}/location/#{query.sanitized_text}" else "#{protocol}://telize-v1.p.mashape.com/location/#{query.sanitized_text}?mashape-key=#{api_key}" end end def supported_protocols [].tap do |array| array << :https array << :http if configuration[:host] end end private # --------------------------------------------------------------- def cache_key(query) query_url(query)[/(.*)\?.*/, 1] end def results(query) # don't look up a loopback or private address, just return the stored result return [reserved_result(query.text)] if query.internal_ip_address? if (doc = fetch_data(query)).nil? or doc['code'] == 401 or empty_result?(doc) [] else [doc] end end def empty_result?(doc) !doc.is_a?(Hash) or doc.keys == ["ip"] end def reserved_result(ip) { "ip" => ip, "latitude" => 0, "longitude" => 0, "city" => "", "timezone" => "", "asn" => 0, "region" => "", "offset" => 0, "organization" => "", "country_code" => "", "country_code3" => "", "postal_code" => "", "continent_code" => "", "country" => "", "region_code" => "" } end def api_key configuration.api_key end end end geocoder-1.5.1/lib/geocoder/lookups/ban_data_gouv_fr.rb0000644000004100000410000000736613503210114023175 0ustar www-datawww-data# encoding: utf-8 require 'geocoder/lookups/base' require 'geocoder/results/ban_data_gouv_fr' module Geocoder::Lookup class BanDataGouvFr < Base def name "Base Adresse Nationale Française" end def map_link_url(coordinates) "https://www.openstreetmap.org/#map=19/#{coordinates.join('/')}" end private # --------------------------------------------------------------- def base_query_url(query) method = query.reverse_geocode? ? "reverse" : "search" "#{protocol}://api-adresse.data.gouv.fr/#{method}/?" end def any_result?(doc) doc['features'].any? end def results(query) if doc = fetch_data(query) and any_result?(doc) [doc] else [] end end #### PARAMS #### def query_url_params(query) query_ban_datagouv_fr_params(query).merge(super) end def query_ban_datagouv_fr_params(query) query.reverse_geocode? ? reverse_geocode_ban_fr_params(query) : search_geocode_ban_fr_params(query) end #### SEARCH GEOCODING PARAMS #### # # :q => required, full text search param) # :limit => force limit number of results returned by raw API # (default = 5) note : only first result is taken # in account in geocoder # # :autocomplete => pass 0 to disable autocomplete treatment of :q # (default = 1) # # :lat => force filter results around specific lat/lon # # :lon => force filter results around specific lat/lon # # :type => force filter the returned result type # (check results for a list of accepted types) # # :postcode => force filter results on a specific city post code # # :citycode => force filter results on a specific city UUID INSEE code # # For up to date doc (in french only) : https://adresse.data.gouv.fr/api/ # def search_geocode_ban_fr_params(query) params = { q: query.sanitized_text } unless (limit = query.options[:limit]).nil? || !limit_param_is_valid?(limit) params[:limit] = limit.to_i end unless (autocomplete = query.options[:autocomplete]).nil? || !autocomplete_param_is_valid?(autocomplete) params[:autocomplete] = autocomplete.to_s end unless (type = query.options[:type]).nil? || !type_param_is_valid?(type) params[:type] = type.downcase end unless (postcode = query.options[:postcode]).nil? || !code_param_is_valid?(postcode) params[:postcode] = postcode.to_s end unless (citycode = query.options[:citycode]).nil? || !code_param_is_valid?(citycode) params[:citycode] = citycode.to_s end params end #### REVERSE GEOCODING PARAMS #### # # :lat => required # # :lon => required # # :type => force returned results type # (check results for a list of accepted types) # def reverse_geocode_ban_fr_params(query) lat_lon = query.coordinates params = { lat: lat_lon.first, lon: lat_lon.last } unless (type = query.options[:type]).nil? || !type_param_is_valid?(type) params[:type] = type.downcase end params end def limit_param_is_valid?(param) param.to_i.positive? end def autocomplete_param_is_valid?(param) [0,1].include?(param.to_i) end def type_param_is_valid?(param) %w(housenumber street locality village town city).include?(param.downcase) end def code_param_is_valid?(param) (1..99999).include?(param.to_i) end end end geocoder-1.5.1/lib/geocoder/lookups/baidu.rb0000644000004100000410000000333413503210114020770 0ustar www-datawww-datarequire 'geocoder/lookups/base' require "geocoder/results/baidu" module Geocoder::Lookup class Baidu < Base def name "Baidu" end def required_api_key_parts ["key"] end # HTTP only def supported_protocols [:http] end private # --------------------------------------------------------------- def base_query_url(query) "#{protocol}://api.map.baidu.com/geocoder/v2/?" end def content_key 'result' end def results(query, reverse = false) return [] unless doc = fetch_data(query) case doc['status'] when 0 return [doc[content_key]] unless doc[content_key].blank? when 1, 3, 4 raise_error(Geocoder::Error, "server error.") || Geocoder.log(:warn, "#{name} Geocoding API error: server error.") when 2 raise_error(Geocoder::InvalidRequest, "invalid request.") || Geocoder.log(:warn, "#{name} Geocoding API error: invalid request.") when 5 raise_error(Geocoder::InvalidApiKey, "invalid api key") || Geocoder.log(:warn, "#{name} Geocoding API error: invalid api key.") when 101, 102, 200..299 raise_error(Geocoder::RequestDenied, "request denied") || Geocoder.log(:warn, "#{name} Geocoding API error: request denied.") when 300..399 raise_error(Geocoder::OverQueryLimitError, "over query limit.") || Geocoder.log(:warn, "#{name} Geocoding API error: over query limit.") end return [] end def query_url_params(query) { (query.reverse_geocode? ? :location : :address) => query.sanitized_text, :ak => configuration.api_key, :output => "json" }.merge(super) end end end geocoder-1.5.1/lib/geocoder/lookups/ipapi_com.rb0000644000004100000410000000374413503210114021651 0ustar www-datawww-datarequire 'geocoder/lookups/base' require 'geocoder/results/ipapi_com' module Geocoder::Lookup class IpapiCom < Base def name "ip-api.com" end def supported_protocols if configuration.api_key [:http, :https] else [:http] end end private # ---------------------------------------------------------------- def base_query_url(query) domain = configuration.api_key ? "pro.ip-api.com" : "ip-api.com" url = "#{protocol}://#{domain}/json/#{query.sanitized_text}" url << "?" if not url_query_string(query).empty? url end def parse_raw_data(raw_data) if raw_data.chomp == "invalid key" invalid_key_result else super(raw_data) end end def results(query) # don't look up a loopback or private address, just return the stored result return [reserved_result(query.text)] if query.internal_ip_address? return [] unless doc = fetch_data(query) if doc["message"] == "invalid key" raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid API key.") return [] else return [doc] end end def reserved_result(query) { "message" => "reserved range", "query" => query, "status" => "fail", "ip" => query, "city" => "", "region_code" => "", "region_name" => "", "metrocode" => "", "zipcode" => "", "latitude" => "0", "longitude" => "0", "country_name" => "Reserved", "country_code" => "RD" } end def invalid_key_result { "message" => "invalid key" } end def query_url_params(query) params = {} params.merge!(fields: configuration[:fields]) if configuration.has_key?(:fields) params.merge!(key: configuration.api_key) if configuration.api_key params.merge(super) end end end geocoder-1.5.1/lib/geocoder/lookups/ipinfo_io.rb0000644000004100000410000000161413503210114021656 0ustar www-datawww-datarequire 'geocoder/lookups/base' require 'geocoder/results/ipinfo_io' module Geocoder::Lookup class IpinfoIo < Base def name "Ipinfo.io" end private # --------------------------------------------------------------- def base_query_url(query) url = "#{protocol}://ipinfo.io/#{query.sanitized_text}/geo" url << "?" if configuration.api_key url end def results(query) # don't look up a loopback or private address, just return the stored result return [reserved_result(query.text)] if query.internal_ip_address? if !(doc = fetch_data(query)).is_a?(Hash) or doc['error'] [] else [doc] end end def reserved_result(ip) { "ip" => ip, "bogon" => true } end def query_url_params(query) { token: configuration.api_key }.merge(super) end end end geocoder-1.5.1/lib/geocoder/lookups/base.rb0000644000004100000410000002465213503210114020624 0ustar www-datawww-datarequire 'net/http' require 'net/https' require 'uri' unless defined?(ActiveSupport::JSON) begin require 'json' rescue LoadError raise LoadError, "Please install the 'json' or 'json_pure' gem to parse geocoder results." end end module Geocoder module Lookup class Base def initialize @cache = nil end ## # Human-readable name of the geocoding API. # def name fail end ## # Symbol which is used in configuration to refer to this Lookup. # def handle str = self.class.to_s str[str.rindex(':')+1..-1].gsub(/([a-z\d]+)([A-Z])/,'\1_\2').downcase.to_sym end ## # Query the geocoding API and return a Geocoder::Result object. # Returns +nil+ on timeout or error. # # Takes a search string (eg: "Mississippi Coast Coliseumf, Biloxi, MS", # "205.128.54.202") for geocoding, or coordinates (latitude, longitude) # for reverse geocoding. Returns an array of Geocoder::Results. # def search(query, options = {}) query = Geocoder::Query.new(query, options) unless query.is_a?(Geocoder::Query) results(query).map{ |r| result = result_class.new(r) result.cache_hit = @cache_hit if cache result } end ## # Return the URL for a map of the given coordinates. # # Not necessarily implemented by all subclasses as only some lookups # also provide maps. # def map_link_url(coordinates) nil end ## # Array containing string descriptions of keys required by the API. # Empty array if keys are optional or not required. # def required_api_key_parts [] end ## # URL to use for querying the geocoding engine. # # Subclasses should not modify this method. Instead they should define # base_query_url and url_query_string. If absolutely necessary to # subclss this method, they must also subclass #cache_key. # def query_url(query) base_query_url(query) + url_query_string(query) end ## # The working Cache object. # def cache if @cache.nil? and store = configuration.cache @cache = Cache.new(store, configuration.cache_prefix) end @cache end ## # Array containing the protocols supported by the api. # Should be set to [:http] if only HTTP is supported # or [:https] if only HTTPS is supported. # def supported_protocols [:http, :https] end private # ------------------------------------------------------------- ## # String which, when concatenated with url_query_string(query) # produces the full query URL. Should include the "?" a the end. # def base_query_url(query) fail end ## # An object with configuration data for this particular lookup. # def configuration Geocoder.config_for_lookup(handle) end ## # Object used to make HTTP requests. # def http_client proxy_name = "#{protocol}_proxy" if proxy = configuration.send(proxy_name) proxy_url = !!(proxy =~ /^#{protocol}/) ? proxy : protocol + '://' + proxy begin uri = URI.parse(proxy_url) rescue URI::InvalidURIError raise ConfigurationError, "Error parsing #{protocol.upcase} proxy URL: '#{proxy_url}'" end Net::HTTP::Proxy(uri.host, uri.port, uri.user, uri.password) else Net::HTTP end end ## # Geocoder::Result object or nil on timeout or other error. # def results(query) fail end def query_url_params(query) query.options[:params] || {} end def url_query_string(query) hash_to_query( query_url_params(query).reject{ |key,value| value.nil? } ) end ## # Key to use for caching a geocoding result. Usually this will be the # request URL, but in cases where OAuth is used and the nonce, # timestamp, etc varies from one request to another, we need to use # something else (like the URL before OAuth encoding). # def cache_key(query) base_query_url(query) + hash_to_query(cache_key_params(query)) end def cache_key_params(query) # omit api_key and token because they may vary among requests query_url_params(query).reject do |key,value| key.to_s.match(/(key|token)/) end end ## # Class of the result objects # def result_class Geocoder::Result.const_get(self.class.to_s.split(":").last) end ## # Raise exception if configuration specifies it should be raised. # Return false if exception not raised. # def raise_error(error, message = nil) exceptions = configuration.always_raise if exceptions == :all or exceptions.include?( error.is_a?(Class) ? error : error.class ) raise error, message else false end end ## # Returns a parsed search result (Ruby hash). # def fetch_data(query) parse_raw_data fetch_raw_data(query) rescue SocketError => err raise_error(err) or Geocoder.log(:warn, "Geocoding API connection cannot be established.") rescue Errno::ECONNREFUSED => err raise_error(err) or Geocoder.log(:warn, "Geocoding API connection refused.") rescue Timeout::Error => err raise_error(err) or Geocoder.log(:warn, "Geocoding API not responding fast enough " + "(use Geocoder.configure(:timeout => ...) to set limit).") end def parse_json(data) if defined?(ActiveSupport::JSON) ActiveSupport::JSON.decode(data) else JSON.parse(data) end rescue unless raise_error(ResponseParseError.new(data)) Geocoder.log(:warn, "Geocoding API's response was not valid JSON") Geocoder.log(:debug, "Raw response: #{data}") end end ## # Parses a raw search result (returns hash or array). # def parse_raw_data(raw_data) parse_json(raw_data) end ## # Protocol to use for communication with geocoding services. # Set in configuration but not available for every service. # def protocol "http" + (use_ssl? ? "s" : "") end def valid_response?(response) (200..399).include?(response.code.to_i) end ## # Fetch a raw geocoding result (JSON string). # The result might or might not be cached. # def fetch_raw_data(query) key = cache_key(query) if cache and body = cache[key] @cache_hit = true else check_api_key_configuration!(query) response = make_api_request(query) check_response_for_errors!(response) body = response.body # apply the charset from the Content-Type header, if possible ct = response['content-type'] if ct && ct['charset'] charset = ct.split(';').select do |s| s['charset'] end.first.to_s.split('=') if charset.length == 2 body.force_encoding(charset.last) rescue ArgumentError end end if cache and valid_response?(response) cache[key] = body end @cache_hit = false end body end def check_response_for_errors!(response) if response.code.to_i == 400 raise_error(Geocoder::InvalidRequest) || Geocoder.log(:warn, "Geocoding API error: 400 Bad Request") elsif response.code.to_i == 401 raise_error(Geocoder::RequestDenied) || Geocoder.log(:warn, "Geocoding API error: 401 Unauthorized") elsif response.code.to_i == 402 raise_error(Geocoder::OverQueryLimitError) || Geocoder.log(:warn, "Geocoding API error: 402 Payment Required") elsif response.code.to_i == 429 raise_error(Geocoder::OverQueryLimitError) || Geocoder.log(:warn, "Geocoding API error: 429 Too Many Requests") elsif response.code.to_i == 503 raise_error(Geocoder::ServiceUnavailable) || Geocoder.log(:warn, "Geocoding API error: 503 Service Unavailable") end end ## # Make an HTTP(S) request to a geocoding API and # return the response object. # def make_api_request(query) uri = URI.parse(query_url(query)) Geocoder.log(:debug, "Geocoder: HTTP request being made for #{uri.to_s}") http_client.start(uri.host, uri.port, use_ssl: use_ssl?, open_timeout: configuration.timeout, read_timeout: configuration.timeout) do |client| configure_ssl!(client) if use_ssl? req = Net::HTTP::Get.new(uri.request_uri, configuration.http_headers) if configuration.basic_auth[:user] and configuration.basic_auth[:password] req.basic_auth( configuration.basic_auth[:user], configuration.basic_auth[:password] ) end client.request(req) end rescue Timeout::Error raise Geocoder::LookupTimeout rescue Errno::EHOSTUNREACH, Errno::ETIMEDOUT, Errno::ENETUNREACH, Errno::ECONNRESET raise Geocoder::NetworkError end def use_ssl? if supported_protocols == [:https] true elsif supported_protocols == [:http] false else configuration.use_https end end def configure_ssl!(client); end def check_api_key_configuration!(query) key_parts = query.lookup.required_api_key_parts if key_parts.size > Array(configuration.api_key).size parts_string = key_parts.size == 1 ? key_parts.first : key_parts raise Geocoder::ConfigurationError, "The #{query.lookup.name} API requires a key to be configured: " + parts_string.inspect end end ## # Simulate ActiveSupport's Object#to_query. # Removes any keys with nil value. # def hash_to_query(hash) require 'cgi' unless defined?(CGI) && defined?(CGI.escape) hash.collect{ |p| p[1].nil? ? nil : p.map{ |i| CGI.escape i.to_s } * '=' }.compact.sort * '&' end end end end geocoder-1.5.1/lib/geocoder/lookups/location_iq.rb0000644000004100000410000000242313503210114022203 0ustar www-datawww-datarequire 'geocoder/lookups/nominatim' require "geocoder/results/location_iq" module Geocoder::Lookup class LocationIq < Nominatim def name "LocationIq" end def required_api_key_parts ["api_key"] end private # ---------------------------------------------------------------- def base_query_url(query) method = query.reverse_geocode? ? "reverse" : "search" "#{protocol}://#{configured_host}/v1/#{method}.php?" end def query_url_params(query) { key: configuration.api_key }.merge(super) end def configured_host configuration[:host] || "locationiq.org" end def results(query) return [] unless doc = fetch_data(query) if !doc.is_a?(Array) case doc['error'] when "Invalid key" raise_error(Geocoder::InvalidApiKey, doc['error']) when "Key not active - Please write to contact@unwiredlabs.com" raise_error(Geocoder::RequestDenied, doc['error']) when "Rate Limited" raise_error(Geocoder::OverQueryLimitError, doc['error']) when "Unknown error - Please try again after some time" raise_error(Geocoder::InvalidRequest, doc['error']) end end doc.is_a?(Array) ? doc : [doc] end end end geocoder-1.5.1/lib/geocoder/lookups/maxmind.rb0000644000004100000410000000450013503210114021335 0ustar www-datawww-datarequire 'geocoder/lookups/base' require 'geocoder/results/maxmind' require 'csv' module Geocoder::Lookup class Maxmind < Base def name "MaxMind" end private # --------------------------------------------------------------- def base_query_url(query) "#{protocol}://geoip.maxmind.com/#{service_code}?" end ## # Return the name of the configured service, or raise an exception. # def configured_service! if s = configuration[:service] and services.keys.include?(s) return s else raise( Geocoder::ConfigurationError, "When using MaxMind you MUST specify a service name: " + "Geocoder.configure(:maxmind => {:service => ...}), " + "where '...' is one of: #{services.keys.inspect}" ) end end def service_code services[configured_service!] end def service_response_fields_count Geocoder::Result::Maxmind.field_names[configured_service!].size end def data_contains_error?(parsed_data) # if all fields given then there is an error parsed_data.size == service_response_fields_count and !parsed_data.last.nil? end ## # Service names mapped to code used in URL. # def services { :country => "a", :city => "b", :city_isp_org => "f", :omni => "e" } end def results(query) # don't look up a loopback or private address, just return the stored result return [reserved_result] if query.internal_ip_address? doc = fetch_data(query) if doc and doc.is_a?(Array) if !data_contains_error?(doc) return [doc] elsif doc.last == "INVALID_LICENSE_KEY" raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid MaxMind API key.") end end return [] end def parse_raw_data(raw_data) # Maxmind just returns text/plain as csv format but according to documentation, # we get ISO-8859-1 encoded string. We need to convert it. CSV.parse_line raw_data.force_encoding("ISO-8859-1").encode("UTF-8") end def reserved_result ",,,,0,0,0,0,,,".split(",") end def query_url_params(query) { :l => configuration.api_key, :i => query.sanitized_text }.merge(super) end end end geocoder-1.5.1/lib/geocoder/lookups/latlon.rb0000644000004100000410000000257113503210114021177 0ustar www-datawww-datarequire 'geocoder/lookups/base' require 'geocoder/results/latlon' module Geocoder::Lookup class Latlon < Base def name "LatLon.io" end def required_api_key_parts ["api_key"] end private # --------------------------------------------------------------- def base_query_url(query) "#{protocol}://latlon.io/api/v1/#{'reverse_' if query.reverse_geocode?}geocode?" end def results(query) return [] unless doc = fetch_data(query) if doc['error'].nil? [doc] # The API returned a 404 response, which indicates no results found elsif doc['error']['type'] == 'api_error' [] elsif doc['error']['type'] == 'authentication_error' raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "LatLon.io service error: invalid API key.") else [] end end def supported_protocols [:https] end private # --------------------------------------------------------------- def query_url_params(query) if query.reverse_geocode? { :token => configuration.api_key, :lat => query.coordinates[0], :lon => query.coordinates[1] }.merge(super) else { :token => configuration.api_key, :address => query.sanitized_text }.merge(super) end end end end geocoder-1.5.1/lib/geocoder/lookups/test.rb0000644000004100000410000000145213503210114020662 0ustar www-datawww-datarequire 'geocoder/lookups/base' require 'geocoder/results/test' module Geocoder module Lookup class Test < Base def name "Test" end def self.add_stub(query_text, results) stubs[query_text] = results end def self.set_default_stub(results) @default_stub = results end def self.read_stub(query_text) stubs.fetch(query_text) { return @default_stub unless @default_stub.nil? raise ArgumentError, "unknown stub request #{query_text}" } end def self.stubs @stubs ||= {} end def self.reset @stubs = {} @default_stub = nil end private def results(query) Geocoder::Lookup::Test.read_stub(query.text) end end end end geocoder-1.5.1/lib/geocoder/lookups/mapbox.rb0000644000004100000410000000305713503210114021174 0ustar www-datawww-datarequire 'geocoder/lookups/base' require "geocoder/results/mapbox" module Geocoder::Lookup class Mapbox < Base def name "Mapbox" end private # --------------------------------------------------------------- def base_query_url(query) "#{protocol}://api.mapbox.com/geocoding/v5/#{dataset}/#{mapbox_search_term(query)}.json?" end def results(query) return [] unless data = fetch_data(query) if data['features'] sort_relevant_feature(data['features']) elsif data['message'] =~ /Invalid\sToken/ raise_error(Geocoder::InvalidApiKey, data['message']) else [] end end def query_url_params(query) {access_token: configuration.api_key}.merge(super(query)) end def mapbox_search_term(query) require 'cgi' unless defined?(CGI) && defined?(CGI.escape) if query.reverse_geocode? lat,lon = query.coordinates "#{CGI.escape lon},#{CGI.escape lat}" else # truncate at first semicolon so Mapbox doesn't go into batch mode # (see Github issue #1299) CGI.escape query.text.to_s.split(';').first.to_s end end def dataset configuration[:dataset] || "mapbox.places" end def supported_protocols [:https] end def sort_relevant_feature(features) # Sort by descending relevance; Favor original order for equal relevance (eg occurs for reverse geocoding) features.sort_by do |feature| [feature["relevance"],-features.index(feature)] end.reverse end end end geocoder-1.5.1/lib/geocoder/lookups/esri.rb0000644000004100000410000000447613503210114020656 0ustar www-datawww-datarequire 'geocoder/lookups/base' require "geocoder/results/esri" require 'geocoder/esri_token' module Geocoder::Lookup class Esri < Base def name "Esri" end private # --------------------------------------------------------------- def base_query_url(query) action = query.reverse_geocode? ? "reverseGeocode" : "find" "#{protocol}://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/#{action}?" end def results(query) return [] unless doc = fetch_data(query) if (!query.reverse_geocode?) return [] if !doc['locations'] || doc['locations'].empty? end if (doc['error'].nil?) return [ doc ] else return [] end end def query_url_params(query) params = { :f => "pjson", :outFields => "*" } if query.reverse_geocode? params[:location] = query.coordinates.reverse.join(',') else params[:text] = query.sanitized_text end params[:token] = token(query) if for_storage_value = for_storage(query) params[:forStorage] = for_storage_value end params[:sourceCountry] = configuration[:source_country] if configuration[:source_country] params.merge(super) end def for_storage(query) if query.options.has_key?(:for_storage) query.options[:for_storage] else configuration[:for_storage] end end def token(query) token_instance = if query.options[:token] query.options[:token] else configuration[:token] end if !valid_token_configured?(token_instance) && configuration.api_key token_instance = create_and_save_token!(query) end token_instance.to_s unless token_instance.nil? end def valid_token_configured?(token_instance) !token_instance.nil? && token_instance.active? end def create_and_save_token!(query) token_instance = create_token if query.options[:token] query.options[:token] = token_instance else Geocoder.merge_into_lookup_config(:esri, token: token_instance) end token_instance end def create_token Geocoder::EsriToken.generate_token(*configuration.api_key) end end end geocoder-1.5.1/lib/geocoder/lookups/google.rb0000644000004100000410000000570613503210114021165 0ustar www-datawww-datarequire 'geocoder/lookups/base' require "geocoder/results/google" module Geocoder::Lookup class Google < Base def name "Google" end def map_link_url(coordinates) "http://maps.google.com/maps?q=#{coordinates.join(',')}" end def supported_protocols # Google requires HTTPS if an API key is used. if configuration.api_key [:https] else [:http, :https] end end private # --------------------------------------------------------------- def base_query_url(query) "#{protocol}://maps.googleapis.com/maps/api/geocode/json?" end def configure_ssl!(client) client.instance_eval { @ssl_context = OpenSSL::SSL::SSLContext.new options = OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3 if OpenSSL::SSL.const_defined?('OP_NO_COMPRESSION') options |= OpenSSL::SSL::OP_NO_COMPRESSION end @ssl_context.set_params({options: options}) } end def valid_response?(response) json = parse_json(response.body) status = json["status"] if json super(response) and ['OK', 'ZERO_RESULTS'].include?(status) end def results(query) return [] unless doc = fetch_data(query) case doc['status']; when "OK" # OK status implies >0 results return doc['results'] when "OVER_QUERY_LIMIT" raise_error(Geocoder::OverQueryLimitError) || Geocoder.log(:warn, "#{name} API error: over query limit.") when "REQUEST_DENIED" raise_error(Geocoder::RequestDenied, doc['error_message']) || Geocoder.log(:warn, "#{name} API error: request denied (#{doc['error_message']}).") when "INVALID_REQUEST" raise_error(Geocoder::InvalidRequest, doc['error_message']) || Geocoder.log(:warn, "#{name} API error: invalid request (#{doc['error_message']}).") end return [] end def query_url_google_params(query) params = { :sensor => "false", :language => (query.language || configuration.language) } if query.options[:google_place_id] params[:place_id] = query.sanitized_text else params[(query.reverse_geocode? ? :latlng : :address)] = query.sanitized_text end unless (bounds = query.options[:bounds]).nil? params[:bounds] = bounds.map{ |point| "%f,%f" % point }.join('|') end unless (region = query.options[:region]).nil? params[:region] = region end unless (components = query.options[:components]).nil? params[:components] = components.is_a?(Array) ? components.join("|") : components end unless (result_type = query.options[:result_type]).nil? params[:result_type] = result_type.is_a?(Array) ? result_type.join("|") : result_type end params end def query_url_params(query) query_url_google_params(query).merge( :key => configuration.api_key ).merge(super) end end end geocoder-1.5.1/lib/geocoder/lookups/mapquest.rb0000644000004100000410000000323313503210114021541 0ustar www-datawww-datarequire 'cgi' require 'geocoder/lookups/base' require "geocoder/results/mapquest" module Geocoder::Lookup class Mapquest < Base def name "Mapquest" end def required_api_key_parts ["key"] end private # --------------------------------------------------------------- def base_query_url(query) domain = configuration[:open] ? "open" : "www" "#{protocol}://#{domain}.mapquestapi.com/geocoding/v1/#{search_type(query)}?" end def search_type(query) query.reverse_geocode? ? "reverse" : "address" end def query_url_params(query) params = { :location => query.sanitized_text } if key = configuration.api_key params[:key] = CGI.unescape(key) end params.merge(super) end # http://www.mapquestapi.com/geocoding/status_codes.html # http://open.mapquestapi.com/geocoding/status_codes.html def results(query) return [] unless doc = fetch_data(query) return doc["results"][0]['locations'] if doc['info']['statuscode'] == 0 # A successful geocode call messages = doc['info']['messages'].join case doc['info']['statuscode'] when 400 # Error with input raise_error(Geocoder::InvalidRequest, messages) || Geocoder.log(:warn, "Mapquest Geocoding API error: #{messages}") when 403 # Key related error raise_error(Geocoder::InvalidApiKey, messages) || Geocoder.log(:warn, "Mapquest Geocoding API error: #{messages}") when 500 # Unknown error raise_error(Geocoder::Error, messages) || Geocoder.log(:warn, "Mapquest Geocoding API error: #{messages}") end [] end end end geocoder-1.5.1/lib/geocoder/lookups/dstk.rb0000644000004100000410000000107213503210114020646 0ustar www-datawww-data# More information about the Data Science Toolkit can be found at: # http://www.datasciencetoolkit.org/. The provided APIs mimic the # Google geocoding api. require 'geocoder/lookups/google' require 'geocoder/results/dstk' module Geocoder::Lookup class Dstk < Google def name "Data Science Toolkit" end private # ---------------------------------------------------------------- def base_query_url(query) host = configuration[:host] || "www.datasciencetoolkit.org" "#{protocol}://#{host}/maps/api/geocode/json?" end end end geocoder-1.5.1/lib/geocoder/lookups/google_places_search.rb0000644000004100000410000000120113503210114024023 0ustar www-datawww-datarequire "geocoder/lookups/google" require "geocoder/results/google_places_search" module Geocoder module Lookup class GooglePlacesSearch < Google def name "Google Places Search" end def required_api_key_parts ["key"] end def supported_protocols [:https] end private def base_query_url(query) "#{protocol}://maps.googleapis.com/maps/api/place/textsearch/json?" end def query_url_google_params(query) { query: query.text, language: query.language || configuration.language } end end end end geocoder-1.5.1/lib/geocoder/lookups/geocodio.rb0000644000004100000410000000230513503210114021471 0ustar www-datawww-datarequire 'geocoder/lookups/base' require "geocoder/results/geocodio" module Geocoder::Lookup class Geocodio < Base def name "Geocodio" end def results(query) return [] unless doc = fetch_data(query) return doc["results"] if doc['error'].nil? if doc['error'] == 'Invalid API key' raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Geocodio service error: invalid API key.") elsif doc['error'].match(/You have reached your daily maximum/) raise_error(Geocoder::OverQueryLimitError, doc['error']) || Geocoder.log(:warn, "Geocodio service error: #{doc['error']}.") else raise_error(Geocoder::InvalidRequest, doc['error']) || Geocoder.log(:warn, "Geocodio service error: #{doc['error']}.") end [] end private # --------------------------------------------------------------- def base_query_url(query) path = query.reverse_geocode? ? "reverse" : "geocode" "#{protocol}://api.geocod.io/v1.3/#{path}?" end def query_url_params(query) { :api_key => configuration.api_key, :q => query.sanitized_text }.merge(super) end end end geocoder-1.5.1/lib/geocoder/lookups/ipstack.rb0000644000004100000410000000302313503210114021335 0ustar www-datawww-datarequire 'geocoder/lookups/base' require 'geocoder/results/ipstack' module Geocoder::Lookup class Ipstack < Base ERROR_CODES = { 404 => Geocoder::InvalidRequest, 101 => Geocoder::InvalidApiKey, 102 => Geocoder::Error, 103 => Geocoder::InvalidRequest, 104 => Geocoder::OverQueryLimitError, 105 => Geocoder::RequestDenied, 301 => Geocoder::InvalidRequest, 302 => Geocoder::InvalidRequest, 303 => Geocoder::RequestDenied, } ERROR_CODES.default = Geocoder::Error def name "Ipstack" end private # ---------------------------------------------------------------- def base_query_url(query) "#{protocol}://#{host}/#{query.sanitized_text}?" end def query_url_params(query) { access_key: configuration.api_key }.merge(super) end def results(query) # don't look up a loopback or private address, just return the stored result return [reserved_result(query.text)] if query.internal_ip_address? return [] unless doc = fetch_data(query) if error = doc['error'] code = error['code'] msg = error['info'] raise_error(ERROR_CODES[code], msg ) || Geocoder.log(:warn, "Ipstack Geocoding API error: #{msg}") return [] end [doc] end def reserved_result(ip) { "ip" => ip, "country_name" => "Reserved", "country_code" => "RD" } end def host configuration[:host] || "api.ipstack.com" end end end geocoder-1.5.1/lib/geocoder/lookups/google_places_details.rb0000644000004100000410000000242413503210114024213 0ustar www-datawww-datarequire "geocoder/lookups/google" require "geocoder/results/google_places_details" module Geocoder module Lookup class GooglePlacesDetails < Google def name "Google Places Details" end def required_api_key_parts ["key"] end def supported_protocols [:https] end private def base_query_url(query) "#{protocol}://maps.googleapis.com/maps/api/place/details/json?" end def results(query) return [] unless doc = fetch_data(query) case doc["status"] when "OK" return [doc["result"]] when "OVER_QUERY_LIMIT" raise_error(Geocoder::OverQueryLimitError) || Geocoder.log(:warn, "Google Places Details API error: over query limit.") when "REQUEST_DENIED" raise_error(Geocoder::RequestDenied) || Geocoder.log(:warn, "Google Places Details API error: request denied.") when "INVALID_REQUEST" raise_error(Geocoder::InvalidRequest) || Geocoder.log(:warn, "Google Places Details API error: invalid request.") end [] end def query_url_google_params(query) { placeid: query.text, language: query.language || configuration.language } end end end end geocoder-1.5.1/lib/geocoder/lookups/pickpoint.rb0000644000004100000410000000157013503210114021704 0ustar www-datawww-datarequire "geocoder/lookups/nominatim" require "geocoder/results/pickpoint" module Geocoder::Lookup class Pickpoint < Nominatim def name "Pickpoint" end def supported_protocols [:https] end def required_api_key_parts ["api_key"] end private # ---------------------------------------------------------------- def base_query_url(query) method = query.reverse_geocode? ? "reverse" : "forward" "#{protocol}://api.pickpoint.io/v1/#{method}?" end def query_url_params(query) params = { key: configuration.api_key }.merge(super) end def results(query) return [] unless doc = fetch_data(query) if !doc.is_a?(Array) && doc['message'] == 'Unauthorized' raise_error(Geocoder::InvalidApiKey, 'Unauthorized') end doc.is_a?(Array) ? doc : [doc] end end end geocoder-1.5.1/lib/geocoder/lookups/nominatim.rb0000644000004100000410000000310313503210114021671 0ustar www-datawww-datarequire 'geocoder/lookups/base' require "geocoder/results/nominatim" module Geocoder::Lookup class Nominatim < Base def name "Nominatim" end def map_link_url(coordinates) "https://www.openstreetmap.org/?lat=#{coordinates[0]}&lon=#{coordinates[1]}&zoom=15&layers=M" end private # --------------------------------------------------------------- def base_query_url(query) method = query.reverse_geocode? ? "reverse" : "search" "#{protocol}://#{configured_host}/#{method}?" end def configured_host configuration[:host] || "nominatim.openstreetmap.org" end def use_ssl? # nominatim.openstreetmap.org redirects HTTP requests to HTTPS if configured_host == "nominatim.openstreetmap.org" true else super end end def results(query) return [] unless doc = fetch_data(query) doc.is_a?(Array) ? doc : [doc] end def parse_raw_data(raw_data) if raw_data.include?("Bandwidth limit exceeded") raise_error(Geocoder::OverQueryLimitError) || Geocoder.log(:warn, "Over API query limit.") else super(raw_data) end end def query_url_params(query) params = { :format => "json", :addressdetails => "1", :"accept-language" => (query.language || configuration.language) }.merge(super) if query.reverse_geocode? lat,lon = query.coordinates params[:lat] = lat params[:lon] = lon else params[:q] = query.sanitized_text end params end end end geocoder-1.5.1/lib/geocoder/lookups/freegeoip.rb0000644000004100000410000000260313503210114021647 0ustar www-datawww-datarequire 'geocoder/lookups/base' require 'geocoder/results/freegeoip' module Geocoder::Lookup class Freegeoip < Base def name "FreeGeoIP" end def supported_protocols if configuration[:host] [:http, :https] else # use https for default host [:https] end end def query_url(query) "#{protocol}://#{host}/json/#{query.sanitized_text}" end private # --------------------------------------------------------------- def cache_key(query) query_url(query) end def parse_raw_data(raw_data) raw_data.match(/^404/) ? nil : super(raw_data) end def results(query) # don't look up a loopback or private address, just return the stored result return [reserved_result(query.text)] if query.internal_ip_address? # note: Freegeoip.net returns plain text "Not Found" on bad request (doc = fetch_data(query)) ? [doc] : [] end def reserved_result(ip) { "ip" => ip, "city" => "", "region_code" => "", "region_name" => "", "metrocode" => "", "zipcode" => "", "latitude" => "0", "longitude" => "0", "country_name" => "Reserved", "country_code" => "RD" } end def host configuration[:host] || "freegeoip.net" end end end �����������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/lookups/pelias.rb�������������������������������������������������������0000644�0000041�0000041�00000003252�13503210114�021160� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/lookups/base' require 'geocoder/results/pelias' module Geocoder::Lookup class Pelias < Base def name 'Pelias' end def endpoint configuration[:endpoint] || 'localhost' end def required_api_key_parts ['search-XXXX'] end private # ---------------------------------------------------------------- def base_query_url(query) query_type = query.reverse_geocode? ? 'reverse' : 'search' "#{protocol}://#{endpoint}/v1/#{query_type}?" end def query_url_params(query) params = { api_key: configuration.api_key }.merge(super) if query.reverse_geocode? lat, lon = query.coordinates params[:'point.lat'] = lat params[:'point.lon'] = lon else params[:text] = query.text end params end def results(query) return [] unless doc = fetch_data(query) # not all responses include a meta if doc['meta'] error = doc.fetch('results', {}).fetch('error', {}) message = error.fetch('type', 'Unknown Error') + ': ' + error.fetch('message', 'No message') log_message = 'Pelias Geocoding API error - ' + message case doc['meta']['status_code'] when '200' # nothing to see here when '403' raise_error(Geocoder::RequestDenied, message) || Geocoder.log(:warn, log_message) when '429' raise_error(Geocoder::OverQueryLimitError, message) || Geocoder.log(:warn, log_message) else raise_error(Geocoder::Error, message) || Geocoder.log(:warn, log_message) end end doc['features'] || [] end end end ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/lookups/db_ip_com.rb����������������������������������������������������0000644�0000041�0000041�00000002410�13503210114�021611� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/lookups/base' require 'geocoder/results/db_ip_com' module Geocoder::Lookup class DbIpCom < Base def name 'DB-IP.com' end def supported_protocols [:https, :http] end def required_api_key_parts ['api_key'] end private # ---------------------------------------------------------------- def base_query_url(query) "#{protocol}://api.db-ip.com/v2/#{configuration.api_key}/#{query.sanitized_text}?" end ## # Same as query_url but without the api key. # def cache_key(query) "#{protocol}://api.db-ip.com/v2/#{query.sanitized_text}?" + hash_to_query(cache_key_params(query)) end def results(query) return [] unless (doc = fetch_data(query)) case doc['error'] when 'maximum number of queries per day exceeded' raise_error Geocoder::OverQueryLimitError || Geocoder.log(:warn, 'DB-API query limit exceeded.') when 'invalid API key' raise_error Geocoder::InvalidApiKey || Geocoder.log(:warn, 'Invalid DB-IP API key.') when nil [doc] else raise_error Geocoder::Error || Geocoder.log(:warn, "Request failed: #{doc['error']}") end end end end ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/lookups/google_premier.rb�����������������������������������������������0000644�0000041�0000041�00000003040�13503210114�022675� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'openssl' require 'base64' require 'geocoder/lookups/google' require 'geocoder/results/google_premier' module Geocoder::Lookup class GooglePremier < Google def name "Google Premier" end def required_api_key_parts ["private key"] end def query_url(query) path = "/maps/api/geocode/json?" + url_query_string(query) "#{protocol}://maps.googleapis.com#{path}&signature=#{sign(path)}" end private # --------------------------------------------------------------- def cache_key(query) "#{protocol}://maps.googleapis.com/maps/api/geocode/json?" + hash_to_query(cache_key_params(query)) end def cache_key_params(query) query_url_google_params(query).merge(super).reject do |k,v| [:key, :client, :channel].include?(k) end end def query_url_params(query) query_url_google_params(query).merge(super).merge( :key => nil, # don't use param inherited from Google lookup :client => configuration.api_key[1], :channel => configuration.api_key[2] ) end def sign(string) raw_private_key = url_safe_base64_decode(configuration.api_key[0]) digest = OpenSSL::Digest.new('sha1') raw_signature = OpenSSL::HMAC.digest(digest, raw_private_key, string) url_safe_base64_encode(raw_signature) end def url_safe_base64_decode(base64_string) Base64.decode64(base64_string.tr('-_', '+/')) end def url_safe_base64_encode(raw) Base64.encode64(raw).tr('+/', '-_').strip end end end ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/lookups/geoip2.rb�������������������������������������������������������0000644�0000041�0000041�00000001760�13503210114�021072� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/lookups/base' require 'geocoder/results/geoip2' module Geocoder module Lookup class Geoip2 < Base attr_reader :gem_name def initialize unless configuration[:file].nil? begin @gem_name = configuration[:lib] || 'maxminddb' require @gem_name rescue LoadError raise "Could not load Maxmind DB dependency. To use the GeoIP2 lookup you must add the #{@gem_name} gem to your Gemfile or have it installed in your system." end @mmdb = db_class.new(configuration[:file].to_s) end super end def name 'GeoIP2' end def required_api_key_parts [] end private def db_class gem_name == 'hive_geoip2' ? Hive::GeoIP2 : MaxMindDB end def results(query) return [] unless configuration[:file] result = @mmdb.lookup(query.to_s) result.nil? ? [] : [result] end end end end ����������������geocoder-1.5.1/lib/geocoder/lookups/geocoder_ca.rb��������������������������������������������������0000644�0000041�0000041�00000002445�13503210114�022140� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/lookups/base' require "geocoder/results/geocoder_ca" module Geocoder::Lookup class GeocoderCa < Base def name "Geocoder.ca" end private # --------------------------------------------------------------- def base_query_url(query) "#{protocol}://geocoder.ca/?" end def results(query) return [] unless doc = fetch_data(query) if doc['error'].nil? return [doc] elsif doc['error']['code'] == "005" # "Postal Code is not in the proper Format" => no results, just shut up else Geocoder.log(:warn, "Geocoder.ca service error: #{doc['error']['code']} (#{doc['error']['description']}).") end return [] end def query_url_params(query) params = { :geoit => "xml", :jsonp => 1, :callback => "test", :auth => configuration.api_key }.merge(super) if query.reverse_geocode? lat,lon = query.coordinates params[:latt] = lat params[:longt] = lon params[:corner] = 1 params[:reverse] = 1 else params[:locate] = query.sanitized_text params[:showpostal] = 1 end params end def parse_raw_data(raw_data) super raw_data[/^test\((.*)\)\;\s*$/, 1] end end end ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/lookups/ip2location.rb��������������������������������������������������0000644�0000041�0000041�00000004517�13503210114�022133� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/lookups/base' require 'geocoder/results/ip2location' module Geocoder::Lookup class Ip2location < Base def name "IP2LocationApi" end def supported_protocols [:http, :https] end private # ---------------------------------------------------------------- def base_query_url(query) "#{protocol}://api.ip2location.com/?" end def query_url_params(query) { key: configuration.api_key ? configuration.api_key : "demo", format: "json", ip: query.sanitized_text }.merge(super) end def results(query) # don't look up a loopback or private address, just return the stored result return [reserved_result(query.text)] if query.internal_ip_address? return [] unless doc = fetch_data(query) if doc["response"] == "INVALID ACCOUNT" raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "INVALID ACCOUNT") return [] else return [doc] end end def reserved_result(query) { "country_code" => "INVALID IP ADDRESS", "country_name" => "INVALID IP ADDRESS", "region_name" => "INVALID IP ADDRESS", "city_name" => "INVALID IP ADDRESS", "latitude" => "INVALID IP ADDRESS", "longitude" => "INVALID IP ADDRESS", "zip_code" => "INVALID IP ADDRESS", "time_zone" => "INVALID IP ADDRESS", "isp" => "INVALID IP ADDRESS", "domain" => "INVALID IP ADDRESS", "net_speed" => "INVALID IP ADDRESS", "idd_code" => "INVALID IP ADDRESS", "area_code" => "INVALID IP ADDRESS", "weather_station_code" => "INVALID IP ADDRESS", "weather_station_name" => "INVALID IP ADDRESS", "mcc" => "INVALID IP ADDRESS", "mnc" => "INVALID IP ADDRESS", "mobile_brand" => "INVALID IP ADDRESS", "elevation" => "INVALID IP ADDRESS", "usage_type" => "INVALID IP ADDRESS" } end def query_url_params(query) params = super if configuration.has_key?(:package) params.merge!(package: configuration[:package]) end params end end end ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/lookups/geocoder_us.rb��������������������������������������������������0000644�0000041�0000041�00000002251�13503210114�022177� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/lookups/base' require "geocoder/results/geocoder_us" module Geocoder::Lookup class GeocoderUs < Base def name "Geocoder.us" end def supported_protocols [:http] end private # ---------------------------------------------------------------- def base_query_url(query) base_query_url_with_optional_key(configuration.api_key) end def cache_key(query) base_query_url_with_optional_key(nil) + url_query_string(query) end def base_query_url_with_optional_key(key = nil) base = "#{protocol}://" if configuration.api_key base << "#{configuration.api_key}@" end base + "geocoder.us/member/service/csv/geocode?" end def results(query) return [] unless doc = fetch_data(query) if doc[0].to_s =~ /^(\d+)\:/ return [] else return [doc.size == 5 ? ((doc[0..1] << nil) + doc[2..4]) : doc] end end def query_url_params(query) (query.text =~ /^\d{5}(?:-\d{4})?$/ ? {:zip => query} : {:address => query.sanitized_text}).merge(super) end def parse_raw_data(raw_data) raw_data.chomp.split(',') end end end �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/lookups/pointpin.rb�����������������������������������������������������0000644�0000041�0000041�00000003561�13503210114�021546� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/lookups/base' require 'geocoder/results/pointpin' module Geocoder::Lookup class Pointpin < Base def name "Pointpin" end def required_api_key_parts ["key"] end def query_url(query) "#{protocol}://geo.pointp.in/#{configuration.api_key}/json/#{query.sanitized_text}" end private # ---------------------------------------------------------------- def cache_key(query) "#{protocol}://geo.pointp.in/json/#{query.sanitized_text}" end def results(query) # don't look up a loopback or private address, just return the stored result return [] if query.internal_ip_address? doc = fetch_data(query) if doc and doc.is_a?(Hash) if !data_contains_error?(doc) return [doc] elsif doc['error'] case doc['error'] when "Invalid IP address" raise_error(Geocoder::InvalidRequest) || Geocoder.log(:warn, "Invalid Pointpin request.") when "Invalid API key" raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid Pointpin API key.") when "Address not found" Geocoder.log(:warn, "Address not found.") end else raise_error(Geocoder::Error) || Geocoder.log(:warn, "Pointpin server error") end end return [] end def data_contains_error?(parsed_data) parsed_data.keys.include?('error') end # TODO: replace this hash with what's actually returned by Pointpin def reserved_result(ip) { "ip" => ip, "city" => "", "region_code" => "", "region_name" => "", "metrocode" => "", "zipcode" => "", "latitude" => "0", "longitude" => "0", "country_name" => "Reserved", "country_code" => "RD" } end end end �����������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/lookups/maxmind_local.rb������������������������������������������������0000644�0000041�0000041�00000004032�13503210114�022507� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'ipaddr' require 'geocoder/lookups/base' require 'geocoder/results/maxmind_local' module Geocoder::Lookup class MaxmindLocal < Base def initialize if !configuration[:file].nil? begin gem = RUBY_PLATFORM == 'java' ? 'jgeoip' : 'geoip' require gem rescue LoadError raise "Could not load geoip dependency. To use MaxMind Local lookup you must add the #{gem} gem to your Gemfile or have it installed in your system." end end super end def name "MaxMind Local" end def required_api_key_parts [] end private def results(query) if configuration[:file] geoip_class = RUBY_PLATFORM == "java" ? JGeoIP : GeoIP result = geoip_class.new(configuration[:file]).city(query.to_s) result.nil? ? [] : [encode_hash(result.to_hash)] elsif configuration[:package] == :city addr = IPAddr.new(query.text).to_i q = "SELECT l.country, l.region, l.city, l.latitude, l.longitude FROM maxmind_geolite_city_location l WHERE l.loc_id = (SELECT b.loc_id FROM maxmind_geolite_city_blocks b WHERE b.start_ip_num <= #{addr} AND #{addr} <= b.end_ip_num)" format_result(q, [:country_name, :region_name, :city_name, :latitude, :longitude]) elsif configuration[:package] == :country addr = IPAddr.new(query.text).to_i q = "SELECT country, country_code FROM maxmind_geolite_country WHERE start_ip_num <= #{addr} AND #{addr} <= end_ip_num" format_result(q, [:country_name, :country_code2]) end end def encode_hash(hash, encoding = "UTF-8") hash.inject({}) do |h,i| h[i[0]] = i[1].is_a?(String) ? i[1].encode(encoding) : i[1] h end end def format_result(query, attr_names) if r = ActiveRecord::Base.connection.execute(query).first r = r.values if r.is_a?(Hash) # some db adapters return Hash, some Array [Hash[*attr_names.zip(r).flatten]] else [] end end end end ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/lookups/bing.rb���������������������������������������������������������0000644�0000041�0000041�00000005303�13503210114�020621� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/lookups/base' require "geocoder/results/bing" module Geocoder::Lookup class Bing < Base def name "Bing" end def map_link_url(coordinates) "http://www.bing.com/maps/default.aspx?cp=#{coordinates.join('~')}" end def required_api_key_parts ["key"] end private # --------------------------------------------------------------- def base_query_url(query) text = CGI.escape(query.sanitized_text.strip) url = "#{protocol}://dev.virtualearth.net/REST/v1/Locations/" if query.reverse_geocode? url + "#{text}?" else if r = query.options[:region] url << "#{r}/" end # use the more forgiving 'unstructured' query format to allow special # chars, newlines, brackets, typos. url + "?q=#{text}&" end end def results(query) return [] unless doc = fetch_data(query) if doc['statusCode'] == 200 return doc['resourceSets'].first['estimatedTotal'] > 0 ? doc['resourceSets'].first['resources'] : [] elsif doc['statusCode'] == 401 and doc["authenticationResultCode"] == "InvalidCredentials" raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid Bing API key.") elsif doc['statusCode'] == 403 raise_error(Geocoder::RequestDenied) || Geocoder.log(:warn, "Bing Geocoding API error: Forbidden Request") elsif [500, 503].include?(doc['statusCode']) raise_error(Geocoder::ServiceUnavailable) || Geocoder.log(:warn, "Bing Geocoding API error: Service Unavailable") else Geocoder.log(:warn, "Bing Geocoding API error: #{doc['statusCode']} (#{doc['statusDescription']}).") end return [] end def query_url_params(query) { key: configuration.api_key, language: (query.language || configuration.language) }.merge(super) end def check_response_for_errors!(response) super if server_overloaded?(response) raise_error(Geocoder::ServiceUnavailable) || Geocoder.log(:warn, "Bing Geocoding API error: Service Unavailable") end end def valid_response?(response) super(response) and not server_overloaded?(response) end def server_overloaded?(response) # Occasionally, the servers processing service requests can be overloaded, # and you may receive some responses that contain no results for queries that # you would normally receive a result. To identify this situation, # check the HTTP headers of the response. If the HTTP header X-MS-BM-WS-INFO is set to 1, # it is best to wait a few seconds and try again. response['x-ms-bm-ws-info'].to_i == 1 end end end �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/lookups/smarty_streets.rb�����������������������������������������������0000644�0000041�0000041�00000003145�13503210114�022774� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/lookups/base' require 'geocoder/results/smarty_streets' module Geocoder::Lookup class SmartyStreets < Base def name "SmartyStreets" end def required_api_key_parts %w(auth-id auth-token) end # required by API as of 26 March 2015 def supported_protocols [:https] end private # --------------------------------------------------------------- def base_query_url(query) if international?(query) "#{protocol}://international-street.api.smartystreets.com/verify?" elsif zipcode_only?(query) "#{protocol}://us-zipcode.api.smartystreets.com/lookup?" else "#{protocol}://us-street.api.smartystreets.com/street-address?" end end def zipcode_only?(query) !query.text.is_a?(Array) and query.to_s.strip =~ /\A\d{5}(-\d{4})?\Z/ end def international?(query) !query.options[:country].nil? end def query_url_params(query) params = {} if international?(query) params[:freeform] = query.sanitized_text params[:country] = query.options[:country] params[:geocode] = true elsif zipcode_only?(query) params[:zipcode] = query.sanitized_text else params[:street] = query.sanitized_text end if configuration.api_key.is_a?(Array) params[:"auth-id"] = configuration.api_key[0] params[:"auth-token"] = configuration.api_key[1] else params[:"auth-token"] = configuration.api_key end params.merge(super) end def results(query) fetch_data(query) || [] end end end ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/lookups/postcode_anywhere_uk.rb�����������������������������������������0000644�0000041�0000041�00000003160�13503210114�024122� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/lookups/base' require 'geocoder/results/postcode_anywhere_uk' module Geocoder::Lookup class PostcodeAnywhereUk < Base # API documentation: http://www.postcodeanywhere.co.uk/Support/WebService/Geocoding/UK/Geocode/2/ DAILY_LIMIT_EXEEDED_ERROR_CODES = ['8', '17'] # api docs say these two codes are the same error INVALID_API_KEY_ERROR_CODE = '2' def name 'PostcodeAnywhereUk' end def required_api_key_parts %w(key) end private # ---------------------------------------------------------------- def base_query_url(query) "#{protocol}://services.postcodeanywhere.co.uk/Geocoding/UK/Geocode/v2.00/json.ws?" end def results(query) response = fetch_data(query) return [] if response.nil? || !response.is_a?(Array) || response.empty? raise_exception_for_response(response[0]) if response[0]['Error'] response end def raise_exception_for_response(response) case response['Error'] when *DAILY_LIMIT_EXEEDED_ERROR_CODES raise_error(Geocoder::OverQueryLimitError, response['Cause']) || Geocoder.log(:warn, response['Cause']) when INVALID_API_KEY_ERROR_CODE raise_error(Geocoder::InvalidApiKey, response['Cause']) || Geocoder.log(:warn, response['Cause']) else # anything else just raise general error with the api cause raise_error(Geocoder::Error, response['Cause']) || Geocoder.log(:warn, response['Cause']) end end def query_url_params(query) { :location => query.sanitized_text, :key => configuration.api_key }.merge(super) end end end ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/lookups/maxmind_geoip2.rb�����������������������������������������������0000644�0000041�0000041�00000003504�13503210114�022605� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/lookups/base' require 'geocoder/results/maxmind_geoip2' module Geocoder::Lookup class MaxmindGeoip2 < Base def name "MaxMind GeoIP2" end # Maxmind's GeoIP2 Precision Services only supports HTTPS, # otherwise a `404 Not Found` HTTP response will be returned def supported_protocols [:https] end def query_url(query) "#{protocol}://geoip.maxmind.com/geoip/v2.1/#{configured_service!}/#{query.sanitized_text.strip}" end private # --------------------------------------------------------------- def cache_key(query) query_url(query) end ## # Return the name of the configured service, or raise an exception. # def configured_service! if s = configuration[:service] and services.include?(s) and configuration[:basic_auth][:user] and configuration[:basic_auth][:password] return s else raise( Geocoder::ConfigurationError, "When using MaxMind GeoIP2 you must specify a service and credentials: Geocoder.configure(maxmind_geoip2: {service: ..., basic_auth: {user: ..., password: ...}}), where service is one of: #{services.inspect}" ) end end def data_contains_error?(doc) (["code", "error"] - doc.keys).empty? end ## # Service names used in URL. # def services [ :country, :city, :insights, ] end def results(query) # don't look up a loopback or private address, just return the stored result return [] if query.internal_ip_address? doc = fetch_data(query) if doc if !data_contains_error?(doc) return [doc] else Geocoder.log(:warn, "MaxMind GeoIP2 Geocoding API error: #{doc['code']} (#{doc['error']}).") end end return [] end end end ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/request.rb��������������������������������������������������������������0000644�0000041�0000041�00000010530�13503210114�017674� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'ipaddr' module Geocoder module Request # The location() method is vulnerable to trivial IP spoofing. # Don't use it in authorization/authentication code, or any # other security-sensitive application. Use safe_location # instead. def location @location ||= Geocoder.search(geocoder_spoofable_ip, ip_address: true).first end # This safe_location() protects you from trivial IP spoofing. # For requests that go through a proxy that you haven't # whitelisted as trusted in your Rack config, you will get the # location for the IP of the last untrusted proxy in the chain, # not the original client IP. You WILL NOT get the location # corresponding to the original client IP for any request sent # through a non-whitelisted proxy. def safe_location @safe_location ||= Geocoder.search(ip, ip_address: true).first end # There's a whole zoo of nonstandard headers added by various # proxy softwares to indicate original client IP. # ANY of these can be trivially spoofed! # (except REMOTE_ADDR, which should by set by your server, # and is included at the end as a fallback. # Order does matter: we're following the convention established in # ActionDispatch::RemoteIp::GetIp::calculate_ip() # https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/remote_ip.rb # where the forwarded_for headers, possibly containing lists, # are arbitrarily preferred over headers expected to contain a # single address. GEOCODER_CANDIDATE_HEADERS = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'HTTP_X_CLIENT_IP', 'HTTP_CLIENT_IP', 'HTTP_X_REAL_IP', 'HTTP_X_CLUSTER_CLIENT_IP', 'REMOTE_ADDR'] def geocoder_spoofable_ip # We could use a more sophisticated IP-guessing algorithm here, # in which we'd try to resolve the use of different headers by # different proxies. The idea is that by comparing IPs repeated # in different headers, you can sometimes decide which header # was used by a proxy further along in the chain, and thus # prefer the headers used earlier. However, the gains might not # be worth the performance tradeoff, since this method is likely # to be called on every request in a lot of applications. GEOCODER_CANDIDATE_HEADERS.each do |header| if @env.has_key? header addrs = geocoder_split_ip_addresses(@env[header]) addrs = geocoder_remove_port_from_addresses(addrs) addrs = geocoder_reject_non_ipv4_addresses(addrs) addrs = geocoder_reject_trusted_ip_addresses(addrs) return addrs.first if addrs.any? end end @env['REMOTE_ADDR'] end private def geocoder_split_ip_addresses(ip_addresses) ip_addresses ? ip_addresses.strip.split(/[,\s]+/) : [] end # use Rack's trusted_proxy?() method to filter out IPs that have # been configured as trusted; includes private ranges by # default. (we don't want every lookup to return the location # of our own proxy/load balancer) def geocoder_reject_trusted_ip_addresses(ip_addresses) ip_addresses.reject { |ip| trusted_proxy?(ip) } end def geocoder_remove_port_from_addresses(ip_addresses) ip_addresses.map do |ip| # IPv4 if ip.count('.') > 0 ip.split(':').first # IPv6 bracket notation elsif match = ip.match(/\[(\S+)\]/) match.captures.first # IPv6 bare notation else ip end end end def geocoder_reject_non_ipv4_addresses(ip_addresses) ips = [] for ip in ip_addresses begin valid_ip = IPAddr.new(ip) rescue valid_ip = false end ips << valid_ip.to_s if valid_ip end return ips.any? ? ips : ip_addresses end end end ActionDispatch::Request.__send__(:include, Geocoder::Request) if defined?(ActionDispatch::Request) Rack::Request.__send__(:include, Geocoder::Request) if defined?(Rack::Request) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/configuration.rb��������������������������������������������������������0000644�0000041�0000041�00000007156�13503210114�021065� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'singleton' require 'geocoder/configuration_hash' module Geocoder ## # Configuration options should be set by passing a hash: # # Geocoder.configure( # :timeout => 5, # :lookup => :yandex, # :api_key => "2a9fsa983jaslfj982fjasd", # :units => :km # ) # def self.configure(options = nil, &block) if !options.nil? Configuration.instance.configure(options) end end ## # Read-only access to the singleton's config data. # def self.config Configuration.instance.data end ## # Read-only access to lookup-specific config data. # def self.config_for_lookup(lookup_name) data = config.clone data.reject!{ |key,value| !Configuration::OPTIONS.include?(key) } if config.has_key?(lookup_name) data.merge!(config[lookup_name]) end data end ## # Merge the given hash into a lookup's existing configuration. # def self.merge_into_lookup_config(lookup_name, options) base = Geocoder.config[lookup_name] Geocoder.configure(lookup_name => base.merge(options)) end class Configuration include Singleton OPTIONS = [ :timeout, :lookup, :ip_lookup, :language, :http_headers, :use_https, :http_proxy, :https_proxy, :api_key, :cache, :cache_prefix, :always_raise, :units, :distances, :basic_auth, :logger, :kernel_logger_level ] attr_accessor :data def self.set_defaults instance.set_defaults end OPTIONS.each do |o| define_method o do @data[o] end define_method "#{o}=" do |value| @data[o] = value end end def configure(options) @data.rmerge!(options) end def initialize # :nodoc @data = Geocoder::ConfigurationHash.new set_defaults end def set_defaults # geocoding options @data[:timeout] = 3 # geocoding service timeout (secs) @data[:lookup] = :nominatim # name of street address geocoding service (symbol) @data[:ip_lookup] = :ipinfo_io # name of IP address geocoding service (symbol) @data[:language] = :en # ISO-639 language code @data[:http_headers] = {} # HTTP headers for lookup @data[:use_https] = false # use HTTPS for lookup requests? (if supported) @data[:http_proxy] = nil # HTTP proxy server (user:pass@host:port) @data[:https_proxy] = nil # HTTPS proxy server (user:pass@host:port) @data[:api_key] = nil # API key for geocoding service @data[:cache] = nil # cache object (must respond to #[], #[]=, and #keys) @data[:cache_prefix] = "geocoder:" # prefix (string) to use for all cache keys @data[:basic_auth] = {} # user and password for basic auth ({:user => "user", :password => "password"}) @data[:logger] = :kernel # :kernel or Logger instance @data[:kernel_logger_level] = ::Logger::WARN # log level, if kernel logger is used # exceptions that should not be rescued by default # (if you want to implement custom error handling); # supports SocketError and Timeout::Error @data[:always_raise] = [] # calculation options @data[:units] = :mi # :mi or :km @data[:distances] = :linear # :linear or :spherical end instance_eval(OPTIONS.map do |option| o = option.to_s <<-EOS def #{o} instance.data[:#{o}] end def #{o}=(value) instance.data[:#{o}] = value end EOS end.join("\n\n")) end end ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/configuration_hash.rb���������������������������������������������������0000644�0000041�0000041�00000000331�13503210114�022054� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'hash_recursive_merge' module Geocoder class ConfigurationHash < Hash include HashRecursiveMerge def method_missing(meth, *args, &block) has_key?(meth) ? self[meth] : super end end end �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/����������������������������������������������������������������0000755�0000041�0000041�00000000000�13503210114�017361� 5����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/opencagedata.rb�������������������������������������������������0000644�0000041�0000041�00000004070�13503210114�022322� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Opencagedata < Base def poi %w[stadium bus_stop tram_stop].each do |key| return @data['components'][key] if @data['components'].key?(key) end return nil end def house_number @data['components']['house_number'] end def address @data['formatted'] end def street %w[road pedestrian highway].each do |key| return @data['components'][key] if @data['components'].key?(key) end return nil end def city %w[city town village hamlet].each do |key| return @data['components'][key] if @data['components'].key?(key) end return nil end def village @data['components']['village'] end def state @data['components']['state'] end def state_code @data['components']['state_code'] end def postal_code @data['components']['postcode'].to_s end def county @data['components']['county'] end def country @data['components']['country'] end def country_code @data['components']['country_code'] end def suburb @data['components']['suburb'] end def coordinates [@data['geometry']['lat'].to_f, @data['geometry']['lng'].to_f] end def viewport bounds = @data['bounds'] || fail south, west = %w(lat lng).map { |i| bounds['southwest'][i] } north, east = %w(lat lng).map { |i| bounds['northeast'][i] } [south, west, north, east] end def time_zone # The OpenCage API documentation states that `annotations` is available # "when possible" https://geocoder.opencagedata.com/api#annotations @data .fetch('annotations', {}) .fetch('timezone', {}) .fetch('name', nil) end def self.response_attributes %w[boundingbox license formatted stadium] end response_attributes.each do |a| unless method_defined?(a) define_method a do @data[a] end end end end end ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/here.rb���������������������������������������������������������0000644�0000041�0000041�00000002777�13503210114�020646� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Here < Base ## # A string in the given format. # def address(format = :full) address_data['Label'] end ## # A two-element array: [lat, lon]. # def coordinates fail unless d = @data['Location']['DisplayPosition'] [d['Latitude'].to_f, d['Longitude'].to_f] end def route address_data['Street'] end def street_number address_data['HouseNumber'] end def state address_data['County'] end def province address_data['County'] end def postal_code address_data['PostalCode'] end def city address_data['City'] end def state_code address_data['State'] end def province_code address_data['State'] end def country fail unless d = address_data['AdditionalData'] if v = d.find{|ad| ad['key']=='CountryName'} return v['value'] end end def country_code address_data['Country'] end def viewport map_view = data['Location']['MapView'] || fail south = map_view['BottomRight']['Latitude'] west = map_view['TopLeft']['Longitude'] north = map_view['TopLeft']['Latitude'] east = map_view['BottomRight']['Longitude'] [south, west, north, east] end private # ---------------------------------------------------------------- def address_data @data['Location']['Address'] || fail end end end �geocoder-1.5.1/lib/geocoder/results/amap.rb���������������������������������������������������������0000644�0000041�0000041�00000003354�13503210114�020631� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Amap < Base def coordinates location = @data['location'] || @data['roadinters'].try(:first).try(:[], 'location') \ || address_components.try(:[], 'streetNumber').try(:[], 'location') location.to_s.split(",").reverse.map(&:to_f) end def address formatted_address end def state province end def province address_components['province'] end def city address_components['city'] == [] ? province : address_components["city"] end def district address_components['district'] end def street if address_components["neighborhood"]["name"] != [] return address_components["neighborhood"]["name"] elsif address_components['township'] != [] return address_components["township"] else return @data['street'] || address_components['streetNumber'].try(:[], 'street') end end def street_number @data['number'] || address_components['streetNumber'].try(:[], 'number') end def formatted_address @data['formatted_address'] end def address_components @data['addressComponent'] || @data end def state_code "" end def postal_code "" end def country "China" end def country_code "CN" end ## # Get address components of a given type. Valid types are defined in # Baidu's Geocoding API documentation and include (among others): # # :business # :cityCode # def self.response_attributes %w[roads pois roadinters] end response_attributes.each do |a| define_method a do @data[a] end end end end������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/ipdata_co.rb����������������������������������������������������0000644�0000041�0000041�00000001157�13503210114�021635� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class IpdataCo < Base def city @data['city'] end def state @data['region'] end def state_code @data['region_code'] end def country @data['country_name'] end def country_code @data['country_code'] end def postal_code @data['postal'] end def self.response_attributes %w[ip asn organisation currency currency_symbol calling_code flag time_zone is_eu] end response_attributes.each do |a| define_method a do @data[a] end end end end �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/geoportail_lu.rb������������������������������������������������0000644�0000041�0000041�00000002321�13503210114�022551� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class GeoportailLu < Base def coordinates geomlonlat['coordinates'].reverse if geolocalized? end def address full_address end def city try_to_extract 'locality', detailled_address end def state 'Luxembourg' end def state_code 'LU' end def postal_code try_to_extract 'zip', detailled_address end def street_address [street_number, street].compact.join(' ') end def street_number try_to_extract 'postnumber', detailled_address end def street try_to_extract 'street', detailled_address end def full_address data['address'] end def geomlonlat data['geomlonlat'] end def detailled_address data['AddressDetails'] end alias_method :country, :state alias_method :province, :state alias_method :country_code, :state_code alias_method :province_code, :state_code private def geolocalized? !!try_to_extract('coordinates', geomlonlat) end def try_to_extract(key, hash) if hash.is_a?(Hash) and hash.include?(key) hash[key] end end end end ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/baidu_ip.rb�����������������������������������������������������0000644�0000041�0000041�00000001453�13503210114�021465� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class BaiduIp < Base def coordinates [point['y'].to_f, point['x'].to_f] end def address @data['address'] end def state province end def province address_detail['province'] end def city address_detail['city'] end def district address_detail['district'] end def street address_detail['street'] end def street_number address_detail['street_number'] end def state_code "" end def postal_code "" end def country "China" end def country_code "CN" end private def address_detail @data['address_detail'] end def point @data['point'] end end end ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/yandex.rb�������������������������������������������������������0000644�0000041�0000041�00000007375�13503210114�021212� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Yandex < Base def coordinates @data['GeoObject']['Point']['pos'].split(' ').reverse.map(&:to_f) end def address(format = :full) @data['GeoObject']['metaDataProperty']['GeocoderMetaData']['text'] end def city if state.empty? and address_details and address_details.has_key? 'Locality' address_details['Locality']['LocalityName'] elsif sub_state.empty? and address_details and address_details.has_key? 'AdministrativeArea' and address_details['AdministrativeArea'].has_key? 'Locality' address_details['AdministrativeArea']['Locality']['LocalityName'] elsif not sub_state_city.empty? sub_state_city else "" end end def country if address_details address_details['CountryName'] else "" end end def country_code if address_details address_details['CountryNameCode'] else "" end end def state if address_details and address_details['AdministrativeArea'] address_details['AdministrativeArea']['AdministrativeAreaName'] else "" end end def sub_state if !state.empty? and address_details and address_details['AdministrativeArea']['SubAdministrativeArea'] address_details['AdministrativeArea']['SubAdministrativeArea']['SubAdministrativeAreaName'] else "" end end def state_code "" end def postal_code "" end def premise_name address_details['Locality']['Premise']['PremiseName'] end def street thoroughfare_data && thoroughfare_data['ThoroughfareName'] end def street_number thoroughfare_data && thoroughfare_data['Premise'] && thoroughfare_data['Premise']['PremiseNumber'] end def kind @data['GeoObject']['metaDataProperty']['GeocoderMetaData']['kind'] end def precision @data['GeoObject']['metaDataProperty']['GeocoderMetaData']['precision'] end def viewport envelope = @data['GeoObject']['boundedBy']['Envelope'] || fail east, north = envelope['upperCorner'].split(' ').map(&:to_f) west, south = envelope['lowerCorner'].split(' ').map(&:to_f) [south, west, north, east] end private # ---------------------------------------------------------------- def thoroughfare_data locality_data && locality_data['Thoroughfare'] end def locality_data dependent_locality && subadmin_locality && admin_locality end def admin_locality address_details && address_details['AdministrativeArea'] && address_details['AdministrativeArea']['Locality'] end def subadmin_locality address_details && address_details['AdministrativeArea'] && address_details['AdministrativeArea']['SubAdministrativeArea'] && address_details['AdministrativeArea']['SubAdministrativeArea']['Locality'] end def dependent_locality address_details && address_details['AdministrativeArea'] && address_details['AdministrativeArea']['SubAdministrativeArea'] && address_details['AdministrativeArea']['SubAdministrativeArea']['Locality'] && address_details['AdministrativeArea']['SubAdministrativeArea']['Locality']['DependentLocality'] end def address_details @data['GeoObject']['metaDataProperty']['GeocoderMetaData']['AddressDetails']['Country'] end def sub_state_city if !sub_state.empty? and address_details and address_details['AdministrativeArea']['SubAdministrativeArea'].has_key? 'Locality' address_details['AdministrativeArea']['SubAdministrativeArea']['Locality']['LocalityName'] || "" else "" end end end end �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/tencent.rb������������������������������������������������������0000644�0000041�0000041�00000003331�13503210114�021346� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Tencent < Base def coordinates ['lat', 'lng'].map{ |i| @data['location'][i] } end def address "#{province}#{city}#{district}#{street}#{street_number}" #@data['title'] or @data['address'] end # NOTE: The Tencent reverse geocoding API has the field named # 'address_component' compared to 'address_components' in the # regular geocoding API. def province @data['address_components'] and (@data['address_components']['province']) or (@data['address_component'] and @data['address_component']['province']) or "" end alias_method :state, :province def city @data['address_components'] and (@data['address_components']['city']) or (@data['address_component'] and @data['address_component']['city']) or "" end def district @data['address_components'] and (@data['address_components']['district']) or (@data['address_component'] and @data['address_component']['district']) or "" end def street @data['address_components'] and (@data['address_components']['street']) or (@data['address_component'] and @data['address_component']['street']) or "" end def street_number @data['address_components'] and (@data['address_components']['street_number']) or (@data['address_component'] and @data['address_component']['street_number']) or "" end def address_components @data['address_components'] or @data['address_component'] end def state_code "" end def postal_code "" end def country "China" end def country_code "CN" end end end�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/postcodes_io.rb�������������������������������������������������0000644�0000041�0000041�00000001113�13503210114�022374� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class PostcodesIo < Base def coordinates [@data['latitude'].to_f, @data['longitude'].to_f] end def quality @data['quality'] end def postal_code @data['postcode'] end alias address postal_code def city @data['admin_ward'] end def county @data['admin_county'] end alias state county def state_code @data['codes']['admin_county'] end def country 'United Kingdom' end def country_code 'UK' end end end �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/telize.rb�������������������������������������������������������0000644�0000041�0000041�00000001142�13503210114�021200� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Telize < Base def city @data['city'] end def state @data['region'] end def state_code @data['region_code'] end def country @data['country'] end def country_code @data['country_code'] end def postal_code @data['postal_code'] end def self.response_attributes %w[timezone isp dma_code area_code ip asn continent_code country_code3] end response_attributes.each do |a| define_method a do @data[a] end end end end ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/ban_data_gouv_fr.rb���������������������������������������������0000644�0000041�0000041�00000014747�13503210114�023203� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# encoding: utf-8 require 'geocoder/results/base' module Geocoder::Result class BanDataGouvFr < Base #### BASE METHODS #### def self.response_attributes %w[limit attribution version licence type features] end response_attributes.each do |a| unless method_defined?(a) define_method a do @data[a] end end end #### BEST RESULT #### def result features[0] if features.any? end #### GEOMETRY #### def geometry result['geometry'] if result end def precision geometry['type'] if geometry end def coordinates coords = geometry["coordinates"] return [coords[1].to_f, coords[0].to_f] end #### PROPERTIES #### # List of raw attrbutes returned by BAN data gouv fr API: # # :id => [string] UUID of the result, said to be not stable # atm, based on IGN reference (Institut national de # l'information géographique et forestière) # # :type => [string] result type (housenumber, street, city, # town, village, locality) # # :score => [float] value between 0 and 1 giving result's # relevancy # # :housenumber => [string] street number and extra information # (bis, ter, A, B) # # :street => [string] street name # # :name => [string] housenumber and street name # # :postcode => [string] city post code (used for mails by La Poste, # beware many cities got severeal postcodes) # # :citycode => [string] city code (INSEE reference, # consider it as a french institutional UUID) # # :city => [string] city name # # :context => [string] department code, department name and # region code # # :label => [string] full address without state, country name # and country code # # CITIES ONLY PROPERTIES # # :adm_weight => [string] administrative weight (importance) of # the city # # :population => [float] number of inhabitants with a 1000 factor # # For up to date doc (in french only) : https://adresse.data.gouv.fr/api/ # def properties result['properties'] if result end # List of usable Geocoder results' methods # # score => [float] result relevance 0 to 1 # # location_id => [string] location's IGN UUID # # result_type => [string] housenumber / street / city # / town / village / locality # # international_address => [string] full address with country code # # national_address => [string] full address with country code # # street_address => [string] housenumber + extra inf # + street name # # street_number => [string] housenumber + extra inf # (bis, ter, etc) # # street_name => [string] street's name # # city_name => [string] city's name # # city_code => [string] city's INSEE UUID # # postal_code => [string] city's postal code (used for mails) # # context => [string] city's department code, department # name and region name # # demartment_name => [string] city's department name # # department_code => [string] city's department INSEE UUID # # region_name => [string] city's region name # # population => [string] city's inhabitants count # # administrative_weight => [integer] city's importance on a scale # from 6 (capital city) to 1 (regular village) # def score properties['score'] end def location_id properties['id'] end # Types # # housenumber # street # city # town # village # locality # def result_type properties['type'] end def international_address "#{national_address}, #{country}" end def national_address properties['label'] end def street_address properties['name'] end def street_number properties['housenumber'] end def street_name properties['street'] end def city_name properties['city'] end def city_code properties['citycode'] end def postal_code properties['postcode'] end def context properties['context'].split(/,/).map(&:strip) end def department_code context[0] if context.length > 0 end # Monkey logic to handle fact Paris is both a city and a department # in Île-de-France region def department_name if context.length > 1 if context[1] == "Île-de-France" "Paris" else context[1] end end end def region_name if context.length == 2 && context[1] == "Île-de-France" context[1] elsif context.length > 2 context[2] end end def country "France" end # Country code types # FR : France # GF : Guyane Française # RE : Réunion # NC : Nouvelle-Calédonie # GP : Guadeloupe # MQ : Martinique # MU : Maurice # PF : Polynésie française # # Will need refacto to handle different country codes, but BAN API # is currently mainly designed for geocode FR country code addresses def country_code "FR" end #### ALIAS METHODS #### alias_method :address, :international_address alias_method :street, :street_name alias_method :city, :city_name alias_method :state, :region_name alias_method :state_code, :state #### CITIES' METHODS #### def population (properties['population'].to_f * 1000).to_i if city?(result_type) end def administrative_weight properties['adm_weight'].to_i if city?(result_type) end private def city?(result_type) %w(village town city).include?(result_type) end end end �������������������������geocoder-1.5.1/lib/geocoder/results/baidu.rb��������������������������������������������������������0000644�0000041�0000041�00000002727�13503210114�021002� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Baidu < Base def coordinates ['lat', 'lng'].map{ |i| @data['location'][i] } end def address @data['formatted_address'] end def province @data['addressComponent'] and @data['addressComponent']['province'] or "" end alias_method :state, :province def city @data['addressComponent'] and @data['addressComponent']['city'] or "" end def district @data['addressComponent'] and @data['addressComponent']['district'] or "" end def street @data['addressComponent'] and @data['addressComponent']['street'] or "" end def street_number @data['addressComponent'] and @data['addressComponent']['street_number'] end def formatted_address @data['formatted_address'] or "" end alias_method :address, :formatted_address def address_components @data['addressComponent'] end def state_code "" end def postal_code "" end def country "China" end def country_code "CN" end ## # Get address components of a given type. Valid types are defined in # Baidu's Geocoding API documentation and include (among others): # # :business # :cityCode # def self.response_attributes %w[business cityCode] end response_attributes.each do |a| define_method a do @data[a] end end end end �����������������������������������������geocoder-1.5.1/lib/geocoder/results/ipapi_com.rb����������������������������������������������������0000644�0000041�0000041�00000001343�13503210114�021647� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class IpapiCom < Base def coordinates [lat, lon] end def address "#{city}, #{state_code} #{postal_code}, #{country}".sub(/^[ ,]*/, "") end def state region_name end def state_code region end def postal_code zip end def country_code @data['countryCode'] end def region_name @data['regionName'] end def self.response_attributes %w[country region city zip timezone isp org as reverse query status message mobile proxy lat lon] end response_attributes.each do |attribute| define_method attribute do @data[attribute] end end end end ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/ipinfo_io.rb����������������������������������������������������0000644�0000041�0000041�00000001355�13503210114�021665� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class IpinfoIo < Base def address(format = :full) "#{city} #{postal_code}, #{country}".sub(/^[ ,]*/, "") end def coordinates @data['loc'].to_s.split(",").map(&:to_f) end def city @data['city'] end def state @data['region'] end def country @data['country'] end def postal_code @data['postal'] end def country_code @data.fetch('country', '') end def state_code @data.fetch('region_code', '') end def self.response_attributes %w(ip region postal) end response_attributes.each do |a| define_method a do @data[a] end end end end �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/base.rb���������������������������������������������������������0000644�0000041�0000041�00000003074�13503210114�020624� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������module Geocoder module Result class Base # data (hash) fetched from geocoding service attr_accessor :data # true if result came from cache, false if from request to geocoding # service; nil if cache is not configured attr_accessor :cache_hit ## # Takes a hash of data from a parsed geocoding service response. # def initialize(data) @data = data @cache_hit = nil end ## # A string in the given format. # # This default implementation dumbly follows the United States address # format and will return incorrect results for most countries. Some APIs # return properly formatted addresses and those should be funneled # through this method. # def address(format = :full) if state_code.to_s != "" s = ", #{state_code}" elsif state.to_s != "" s = ", #{state}" else s = "" end "#{city}#{s} #{postal_code}, #{country}".sub(/^[ ,]*/, '') end ## # A two-element array: [lat, lon]. # def coordinates [@data['latitude'].to_f, @data['longitude'].to_f] end def latitude coordinates[0] end def longitude coordinates[1] end def state fail end def province state end def state_code fail end def province_code state_code end def country fail end def country_code fail end end end end ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/location_iq.rb��������������������������������������������������0000644�0000041�0000041�00000000146�13503210114�022210� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/nominatim' module Geocoder::Result class LocationIq < Nominatim end end��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/maxmind.rb������������������������������������������������������0000644�0000041�0000041�00000005214�13503210114�021345� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Maxmind < Base ## # Hash mapping service names to names of returned fields. # def self.field_names { :country => [ :country_code, :error ], :city => [ :country_code, :region_code, :city_name, :latitude, :longitude, :error ], :city_isp_org => [ :country_code, :region_code, :city_name, :postal_code, :latitude, :longitude, :metro_code, :area_code, :isp_name, :organization_name, :error ], :omni => [ :country_code, :country_name, :region_code, :region_name, :city_name, :latitude, :longitude, :metro_code, :area_code, :time_zone, :continent_code, :postal_code, :isp_name, :organization_name, :domain, :as_number, :netspeed, :user_type, :accuracy_radius, :country_confidence_factor, :city_confidence_factor, :region_confidence_factor, :postal_confidence_factor, :error ] } end ## # Name of the MaxMind service being used. # def service_name # it would be much better to infer this from the length of the @data # array, but MaxMind seems to send inconsistent and wide-ranging response # lengths (see https://github.com/alexreisner/geocoder/issues/396) Geocoder.config.maxmind[:service] end def field_names self.class.field_names[service_name] end def data_hash @data_hash ||= Hash[*field_names.zip(@data).flatten] end def coordinates [data_hash[:latitude].to_f, data_hash[:longitude].to_f] end def city data_hash[:city_name] end def state # not given by MaxMind data_hash[:region_name] || data_hash[:region_code] end def state_code data_hash[:region_code] end def country #not given by MaxMind data_hash[:country_name] || data_hash[:country_code] end def country_code data_hash[:country_code] end def postal_code data_hash[:postal_code] end def method_missing(method, *args, &block) if field_names.include?(method) data_hash[method] else super end end def respond_to?(method) if field_names.include?(method) true else super end end end end ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/latlon.rb�������������������������������������������������������0000644�0000041�0000041�00000002273�13503210114�021203� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Latlon < Base def city address_components["city"] end def coordinates [@data['lat'].to_f, @data['lon'].to_f] end def country "United States" # LatLon.io only supports the US end def country_code "US" # LatLon.io only supports the US end def formatted_address(format = :full) address_components["address"] end alias_method :address, :formatted_address def number address_components["number"] end def prefix address_components["prefix"] end def state address_components["state"] end alias_method :state_code, :state def street [street_name, street_type].compact.join(' ') end def street_name address_components["street_name"] end def street_type address_components["street_type"] end def suffix address_components["suffix"] end def unit address_components["unit"] end def zip address_components["zip"] end alias_method :postal_code, :zip private def address_components @data["address"] || {} end end end �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/test.rb���������������������������������������������������������0000644�0000041�0000041�00000001426�13503210114�020670� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder module Result class Test < Base def self.add_result_attribute(attr) begin remove_method(attr) if method_defined?(attr) rescue NameError # method defined on superclass end define_method(attr) do @data[attr.to_s] || @data[attr.to_sym] end end %w[coordinates neighborhood city state state_code sub_state sub_state_code province province_code postal_code country country_code address street_address street_number route geometry].each do |attr| add_result_attribute(attr) end def initialize(data) data.each_key do |attr| Test.add_result_attribute(attr) end super end end end end ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/mapbox.rb�������������������������������������������������������0000644�0000041�0000041�00000001650�13503210114�021176� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Mapbox < Base def coordinates data['geometry']['coordinates'].reverse.map(&:to_f) end def place_name data['text'] end def street data['properties']['address'] end def city context_part('place') end def state context_part('region') end alias_method :state_code, :state def postal_code context_part('postcode') end def country context_part('country') end alias_method :country_code, :country def neighborhood context_part('neighborhood') end def address [place_name, street, city, state, postal_code, country].compact.join(', ') end private def context_part(name) context.map { |c| c['text'] if c['id'] =~ Regexp.new(name) }.compact.first end def context Array(data['context']) end end end ����������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/esri.rb���������������������������������������������������������0000644�0000041�0000041�00000002770�13503210114�020656� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Esri < Base def address address_key = reverse_geocode? ? 'Address' : 'Match_addr' attributes[address_key] end def city if !reverse_geocode? && is_city? place_name else attributes['City'] end end def state_code attributes['Region'] end alias_method :state, :state_code def country country_key = reverse_geocode? ? "CountryCode" : "Country" attributes[country_key] end alias_method :country_code, :country def postal_code attributes['Postal'] end def place_name place_name_key = reverse_geocode? ? "Address" : "PlaceName" attributes[place_name_key] end def place_type reverse_geocode? ? "Address" : attributes['Type'] end def coordinates [geometry["y"], geometry["x"]] end def viewport north = attributes['Ymax'] south = attributes['Ymin'] east = attributes['Xmax'] west = attributes['Xmin'] [south, west, north, east] end private def attributes reverse_geocode? ? @data['address'] : @data['locations'].first['feature']['attributes'] end def geometry reverse_geocode? ? @data["location"] : @data['locations'].first['feature']["geometry"] end def reverse_geocode? @data['locations'].nil? end def is_city? ['City', 'State Capital', 'National Capital'].include?(place_type) end end end ��������geocoder-1.5.1/lib/geocoder/results/google.rb�������������������������������������������������������0000644�0000041�0000041�00000006251�13503210114�021166� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Google < Base def coordinates ['lat', 'lng'].map{ |i| geometry['location'][i] } end def address(format = :full) formatted_address end def neighborhood if neighborhood = address_components_of_type(:neighborhood).first neighborhood['long_name'] end end def city fields = [:locality, :sublocality, :administrative_area_level_3, :administrative_area_level_2] fields.each do |f| if entity = address_components_of_type(f).first return entity['long_name'] end end return nil # no appropriate components found end def state if state = address_components_of_type(:administrative_area_level_1).first state['long_name'] end end def state_code if state = address_components_of_type(:administrative_area_level_1).first state['short_name'] end end def sub_state if state = address_components_of_type(:administrative_area_level_2).first state['long_name'] end end def sub_state_code if state = address_components_of_type(:administrative_area_level_2).first state['short_name'] end end def country if country = address_components_of_type(:country).first country['long_name'] end end def country_code if country = address_components_of_type(:country).first country['short_name'] end end def postal_code if postal = address_components_of_type(:postal_code).first postal['long_name'] end end def route if route = address_components_of_type(:route).first route['long_name'] end end def street_number if street_number = address_components_of_type(:street_number).first street_number['long_name'] end end def street_address [street_number, route].compact.join(' ') end def types @data['types'] end def formatted_address @data['formatted_address'] end def address_components @data['address_components'] end ## # Get address components of a given type. Valid types are defined in # Google's Geocoding API documentation and include (among others): # # :street_number # :locality # :neighborhood # :route # :postal_code # def address_components_of_type(type) address_components.select{ |c| c['types'].include?(type.to_s) } end def geometry @data['geometry'] end def precision geometry['location_type'] if geometry end def partial_match @data['partial_match'] end def place_id @data['place_id'] end def viewport viewport = geometry['viewport'] || fail bounding_box_from viewport end def bounds bounding_box_from geometry['bounds'] end private def bounding_box_from(box) return nil unless box south, west = %w(lat lng).map { |c| box['southwest'][c] } north, east = %w(lat lng).map { |c| box['northeast'][c] } [south, west, north, east] end end end �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/mapquest.rb�����������������������������������������������������0000644�0000041�0000041�00000001453�13503210114�021550� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Mapquest < Base def coordinates %w[lat lng].map{ |l| @data["latLng"][l] } end def city @data['adminArea5'] end def street @data['street'] end def state @data['adminArea3'] end def county @data['adminArea4'] end alias_method :state_code, :state #FIXME: these might not be right, unclear with MQ documentation alias_method :province, :state alias_method :province_code, :state def postal_code @data['postalCode'].to_s end def country @data['adminArea1'] end def country_code country end def address [street, city, state, postal_code, country].reject{|s| s.length == 0 }.join(", ") end end end ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/dstk.rb���������������������������������������������������������0000644�0000041�0000041�00000000132�13503210114�020647� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/google' module Geocoder::Result class Dstk < Google end end��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/google_places_search.rb�����������������������������������������0000644�0000041�0000041�00000001151�13503210114�024034� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require "geocoder/results/google" module Geocoder module Result class GooglePlacesSearch < Google def types @data["types"] || [] end def rating @data["rating"] end def photos @data["photos"] end def city "" end def state "" end def state_code "" end def province "" end def province_code "" end def postal_code "" end def country "" end def country_code "" end end end end �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/geocodio.rb�����������������������������������������������������0000644�0000041�0000041�00000002706�13503210114�021503� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Geocodio < Base def number address_components["number"] end def street address_components["street"] end def suffix address_components["suffix"] end def street_address [number, address_components["formatted_street"]].compact.join(' ') end def state address_components["state"] end alias_method :state_code, :state def zip # Postal code is not returned for Canada geocode results address_components["zip"] || "" end alias_method :postal_code, :zip def country # Geocodio supports US and Canada, however they don't return the full # country name. if country_code == "CA" "Canada" else "United States" end end def country_code address_components['country'] end def city address_components["city"] end def postdirectional address_components["postdirectional"] end def location @data['location'] end def coordinates ['lat', 'lng'].map{ |i| location[i].to_f } if location end def accuracy @data['accuracy'].to_f if @data.key?('accuracy') end def formatted_address(format = :full) @data['formatted_address'] end alias_method :address, :formatted_address private def address_components @data['address_components'] || {} end end end ����������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/ipstack.rb������������������������������������������������������0000644�0000041�0000041�00000002443�13503210114�021347� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Ipstack < Base def address(format = :full) s = region_code.empty? ? "" : ", #{region_code}" "#{city}#{s} #{zip}, #{country_name}".sub(/^[ ,]*/, "") end def state @data['region_name'] end def state_code @data['region_code'] end def country @data['country_name'] end def postal_code @data['zip'] || @data['zipcode'] || @data['zip_code'] end def self.response_attributes [ ['ip', ''], ['hostname', ''], ['continent_code', ''], ['continent_name', ''], ['country_code', ''], ['country_name', ''], ['region_code', ''], ['region_name', ''], ['city', ''], ['zip', ''], ['latitude', 0], ['longitude', 0], ['location', {}], ['time_zone', {}], ['currency', {}], ['connection', {}], ['security', {}], ] end response_attributes.each do |attr, default| define_method attr do @data[attr] || default end end def metro_code Geocoder.log(:warn, "Ipstack does not implement `metro_code` in api results. Please discontinue use.") 0 # no longer implemented by ipstack end end end �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/google_places_details.rb����������������������������������������0000644�0000041�0000041�00000001131�13503210114�024212� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require "geocoder/results/google" module Geocoder module Result class GooglePlacesDetails < Google def place_id @data["place_id"] end def types @data["types"] || [] end def reviews @data["reviews"] || [] end def rating @data["rating"] end def rating_count @data["user_ratings_total"] end def phone_number @data["international_phone_number"] end def website @data["website"] end def photos @data["photos"] end end end end ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/pickpoint.rb����������������������������������������������������0000644�0000041�0000041�00000000145�13503210114�021706� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/nominatim' module Geocoder::Result class Pickpoint < Nominatim end end���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/nominatim.rb����������������������������������������������������0000644�0000041�0000041�00000003676�13503210114�021715� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Nominatim < Base def poi return @data['address'][place_type] if @data['address'].key?(place_type) return nil end def house_number @data['address']['house_number'] end def address @data['display_name'] end def street %w[road pedestrian highway].each do |key| return @data['address'][key] if @data['address'].key?(key) end return nil end def city %w[city town village hamlet].each do |key| return @data['address'][key] if @data['address'].key?(key) end return nil end def village @data['address']['village'] end def town @data['address']['town'] end def state @data['address']['state'] end alias_method :state_code, :state def postal_code @data['address']['postcode'] end def county @data['address']['county'] end def country @data['address']['country'] end def country_code @data['address']['country_code'] end def suburb @data['address']['suburb'] end def city_district @data['address']['city_district'] end def state_district @data['address']['state_district'] end def neighbourhood @data['address']['neighbourhood'] end def coordinates [@data['lat'].to_f, @data['lon'].to_f] end def place_class @data['class'] end def place_type @data['type'] end def viewport south, north, west, east = @data['boundingbox'].map(&:to_f) [south, west, north, east] end def self.response_attributes %w[place_id osm_type osm_id boundingbox license polygonpoints display_name class type stadium] end response_attributes.each do |a| unless method_defined?(a) define_method a do @data[a] end end end end end ������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/freegeoip.rb����������������������������������������������������0000644�0000041�0000041�00000001112�13503210114�021646� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Freegeoip < Base def city @data['city'] end def state @data['region_name'] end def state_code @data['region_code'] end def country @data['country_name'] end def country_code @data['country_code'] end def postal_code @data['zipcode'] || @data['zip_code'] end def self.response_attributes %w[metro_code ip] end response_attributes.each do |a| define_method a do @data[a] end end end end ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/pelias.rb�������������������������������������������������������0000644�0000041�0000041�00000001575�13503210114�021173� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Pelias < Base def address(format = :full) properties['label'] end def city locality end def coordinates geometry['coordinates'].reverse end def country_code properties['country_a'] end def postal_code properties['postalcode'].to_s end def province state end def state properties['region'] end def state_code properties['region_a'] end def self.response_attributes %w[county confidence country gid id layer localadmin locality neighborhood] end response_attributes.each do |a| define_method a do properties[a] end end private def geometry @data.fetch('geometry', {}) end def properties @data.fetch('properties', {}) end end end �����������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/db_ip_com.rb����������������������������������������������������0000644�0000041�0000041�00000001674�13503210114�021631� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class DbIpCom < Base def coordinates ['latitude', 'longitude'].map{ |coordinate_name| @data[coordinate_name] } end def city @data['city'] end def district @data['district'] end def state_code @data['stateProv'] end alias_method :state, :state_code def zip_code @data['zipCode'] end alias_method :postal_code, :zip_code def country_name @data['countryName'] end alias_method :country, :country_name def country_code @data['countryCode'] end def continent_name @data['continentName'] end alias_method :continent, :continent_name def continent_code @data['continentCode'] end def time_zone @data['timeZone'] end def gmt_offset @data['gmtOffset'] end def currency_code @data['currencyCode'] end end end ��������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/google_premier.rb�����������������������������������������������0000644�0000041�0000041�00000000143�13503210114�022703� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/google' module Geocoder::Result class GooglePremier < Google end end�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/geoip2.rb�������������������������������������������������������0000644�0000041�0000041�00000002630�13503210114�021074� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder module Result class Geoip2 < Base def coordinates %w[latitude longitude].map do |l| data.fetch('location', {}).fetch(l, 0.0) end end def city fetch_name( data.fetch('city', {}).fetch('names', {}) ) end def state fetch_name( data.fetch('subdivisions', []).fetch(0, {}).fetch('names', {}) ) end def state_code data.fetch('subdivisions', []).fetch(0, {}).fetch('iso_code', '') end def country fetch_name( data.fetch('country', {}).fetch('names', {}) ) end def country_code data.fetch('country', {}).fetch('iso_code', '') end def postal_code data.fetch('postal', {}).fetch('code', '') end def self.response_attributes %w[ip] end response_attributes.each do |a| define_method a do @data[a] end end def language=(l) @language = l.to_s end def language @language ||= default_language end def data @data.to_hash end private def default_language @default_language = Geocoder.config[:language].to_s end def fetch_name(names) names[language] || names[default_language] || '' end end end end ��������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/geocoder_ca.rb��������������������������������������������������0000644�0000041�0000041�00000002640�13503210114�022142� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class GeocoderCa < Base def coordinates [@data['latt'].to_f, @data['longt'].to_f] end def address(format = :full) "#{street_address}, #{city}, #{state} #{postal_code}, #{country}".sub(/^[ ,]*/, "") end def street_address "#{@data['stnumber']} #{@data['staddress']}" end def city @data['city'] or (@data['standard'] and @data['standard']['city']) or "" end def state @data['prov'] or (@data['standard'] and @data['standard']['prov']) or "" end alias_method :state_code, :state def postal_code @data['postal'] or (@data['standard'] and @data['standard']['postal']) or "" end def country country_code == 'CA' ? 'Canada' : 'United States' end def country_code return nil if state.nil? || state == "" canadian_province_abbreviations.include?(state) ? "CA" : "US" end def self.response_attributes %w[latt longt inlatt inlongt distance stnumber staddress prov NearRoad NearRoadDistance betweenRoad1 betweenRoad2 intersection major_intersection] end response_attributes.each do |a| define_method a do @data[a] end end private # ---------------------------------------------------------------- def canadian_province_abbreviations %w[ON QC NS NB MB BC PE SK AB NL] end end end ������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/ip2location.rb��������������������������������������������������0000644�0000041�0000041�00000001121�13503210114�022124� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Ip2location < Base def address(format = :full) "#{city_name} #{zip_code}, #{country_name}".sub(/^[ ,]*/, '') end def self.response_attributes %w[country_code country_name region_name city_name latitude longitude zip_code time_zone isp domain net_speed idd_code area_code usage_type weather_station_code weather_station_name mcc mnc mobile_brand elevation] end response_attributes.each do |attr| define_method attr do @data[attr] || "" end end end end �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/geocoder_us.rb��������������������������������������������������0000644�0000041�0000041�00000001105�13503210114�022201� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class GeocoderUs < Base def coordinates [@data[0].to_f, @data[1].to_f] end def address(format = :full) "#{street_address}, #{city}, #{state} #{postal_code}, #{country}".sub(/^[ ,]*/, "") end def street_address @data[2] end def city @data[3] end def state @data[4] end alias_method :state_code, :state def postal_code @data[5] end def country 'United States' end def country_code 'US' end end end �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/pointpin.rb�����������������������������������������������������0000644�0000041�0000041�00000001324�13503210114�021546� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Pointpin < Base def address [ city_name, state, postal_code, country ].select{ |i| i.to_s != "" }.join(", ") end def city @data['city_name'] end def state @data['region_name'] end def state_code @data['region_code'] end def country @data['country_name'] end def postal_code @data['postcode'] end def self.response_attributes %w[continent_code ip country_code country_name region_name city_name postcode latitude longitude time_zone languages] end response_attributes.each do |a| define_method a do @data[a] end end end end ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/maxmind_local.rb������������������������������������������������0000644�0000041�0000041�00000001217�13503210114�022516� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class MaxmindLocal < Base def coordinates [@data[:latitude], @data[:longitude]] end def city @data[:city_name] end def state @data[:region_name] end def state_code "" # Not available in Maxmind's database end def country @data[:country_name] end def country_code @data[:country_code2] end def postal_code @data[:postal_code] end def self.response_attributes %w[ip] end response_attributes.each do |a| define_method a do @data[a] end end end end ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/bing.rb���������������������������������������������������������0000644�0000041�0000041�00000001531�13503210114�020625� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class Bing < Base def address(format = :full) @data['address']['formattedAddress'] end def city @data['address']['locality'] end def state_code @data['address']['adminDistrict'] end alias_method :state, :state_code def country @data['address']['countryRegion'] end alias_method :country_code, :country def postal_code @data['address']['postalCode'].to_s end def coordinates @data['point']['coordinates'] end def address_data @data['address'] end def viewport @data['bbox'] end def self.response_attributes %w[bbox name confidence entityType] end response_attributes.each do |a| define_method a do @data[a] end end end end �����������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/smarty_streets.rb�����������������������������������������������0000644�0000041�0000041�00000005370�13503210114�023003� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/lookups/base' module Geocoder::Result class SmartyStreets < Base def coordinates result = %w(latitude longitude).map do |i| zipcode_endpoint? ? zipcodes.first[i] : metadata[i] end if result.compact.empty? nil else result end end def address parts = if international_endpoint? (1..12).map { |i| @data["address#{i}"] } else [ delivery_line_1, delivery_line_2, last_line ] end parts.select{ |i| i.to_s != "" }.join(" ") end def state if international_endpoint? components['administrative_area'] elsif zipcode_endpoint? city_states.first['state'] else components['state_abbreviation'] end end def state_code if international_endpoint? components['administrative_area'] elsif zipcode_endpoint? city_states.first['state_abbreviation'] else components['state_abbreviation'] end end def country international_endpoint? ? components['country_iso_3'] : "United States" end def country_code international_endpoint? ? components['country_iso_3'] : "US" end ## Extra methods not in base.rb ------------------------ def street international_endpoint? ? components['thoroughfare_name'] : components['street_name'] end def city if international_endpoint? components['locality'] elsif zipcode_endpoint? city_states.first['city'] else components['city_name'] end end def zipcode if international_endpoint? components['postal_code'] elsif zipcode_endpoint? zipcodes.first['zipcode'] else components['zipcode'] end end alias_method :postal_code, :zipcode def zip4 components['plus4_code'] end alias_method :postal_code_extended, :zip4 def fips zipcode_endpoint? ? zipcodes.first['county_fips'] : metadata['county_fips'] end def zipcode_endpoint? zipcodes.any? end def international_endpoint? !@data['address1'].nil? end [ :delivery_line_1, :delivery_line_2, :last_line, :delivery_point_barcode, :addressee ].each do |m| define_method(m) do @data[m.to_s] || '' end end [ :components, :metadata, :analysis ].each do |m| define_method(m) do @data[m.to_s] || {} end end [ :city_states, :zipcodes ].each do |m| define_method(m) do @data[m.to_s] || [] end end end end ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/postcode_anywhere_uk.rb�����������������������������������������0000644�0000041�0000041�00000001523�13503210114�024130� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/base' module Geocoder::Result class PostcodeAnywhereUk < Base def coordinates [@data['Latitude'].to_f, @data['Longitude'].to_f] end def blank_result '' end alias_method :state, :blank_result alias_method :state_code, :blank_result alias_method :postal_code, :blank_result def address @data['Location'] end def city # is this too big a jump to assume that the API always # returns a City, County as the last elements? city = @data['Location'].split(',')[-2] || blank_result city.strip end def os_grid @data['OsGrid'] end # This is a UK only API; all results are UK specific and # so ommitted from API response. def country 'United Kingdom' end def country_code 'UK' end end end �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/results/maxmind_geoip2.rb�����������������������������������������������0000644�0000041�0000041�00000000633�13503210114�022612� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder/results/geoip2' module Geocoder::Result class MaxmindGeoip2 < Geoip2 # MindmindGeoip2 has the same results as Geoip2 because both are from MaxMind's GeoIP2 Precision Services # See http://dev.maxmind.com/geoip/geoip2/web-services/ The difference being that Maxmind calls the service # directly while GeoIP2 uses Hive::GeoIP2. See https://github.com/desuwa/hive_geoip2 end end �����������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/esri_token.rb�����������������������������������������������������������0000644�0000041�0000041�00000001717�13503210114�020355� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������module Geocoder class EsriToken attr_accessor :value, :expires_at def initialize(value, expires_at) @value = value @expires_at = expires_at end def to_s @value end def active? @expires_at > Time.now end def self.generate_token(client_id, client_secret, expires=1440) # creates a new token that will expire in 1 day by default getToken = Net::HTTP.post_form URI('https://www.arcgis.com/sharing/rest/oauth2/token'), f: 'json', client_id: client_id, client_secret: client_secret, grant_type: 'client_credentials', expiration: expires # (minutes) max: 20160, default: 1 day response = JSON.parse(getToken.body) if response['error'] Geocoder.log(:warn, response['error']) else token_value = response['access_token'] expires_at = Time.now + (expires * 60) new(token_value, expires_at) end end end end �������������������������������������������������geocoder-1.5.1/lib/geocoder/cli.rb������������������������������������������������������������������0000644�0000041�0000041�00000007562�13503210114�016766� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'geocoder' require 'optparse' module Geocoder class Cli def self.run(args, out = STDOUT) show_url = false show_json = false # remove arguments that are probably coordinates so they are not # processed as arguments (eg: -31.96047031,115.84274631) coords = args.select{ |i| i.match(/^-\d/) } args -= coords OptionParser.new{ |opts| opts.banner = "Usage:\n geocode [options] <location>" opts.separator "\nOptions: " opts.on("-k <key>", "--key <key>", "Key for geocoding API (usually optional). Enclose multi-part keys in quotes and separate parts by spaces") do |key| if (key_parts = key.split(/\s+/)).size > 1 Geocoder.configure(:api_key => key_parts) else Geocoder.configure(:api_key => key) end end opts.on("-l <language>", "--language <language>", "Language of output (see API docs for valid choices)") do |language| Geocoder.configure(:language => language) end opts.on("-p <proxy>", "--proxy <proxy>", "HTTP proxy server to use (user:pass@host:port)") do |proxy| Geocoder.configure(:http_proxy => proxy) end opts.on("-s <service>", Geocoder::Lookup.all_services_except_test, "--service <service>", "Geocoding service: #{Geocoder::Lookup.all_services_except_test * ', '}") do |service| Geocoder.configure(:lookup => service.to_sym) Geocoder.configure(:ip_lookup => service.to_sym) end opts.on("-t <seconds>", "--timeout <seconds>", "Maximum number of seconds to wait for API response") do |timeout| Geocoder.configure(:timeout => timeout.to_i) end opts.on("-j", "--json", "Print API's raw JSON response") do show_json = true end opts.on("-u", "--url", "Print URL for API query instead of result") do show_url = true end opts.on_tail("-v", "--version", "Print version number") do require "geocoder/version" out << "Geocoder #{Geocoder::VERSION}\n" exit end opts.on_tail("-h", "--help", "Print this help") do out << "Look up geographic information about a location.\n\n" out << opts out << "\nCreated and maintained by Alex Reisner, available under the MIT License.\n" out << "Report bugs and contribute at http://github.com/alexreisner/geocoder\n" exit end }.parse!(args) # concatenate args with coords that might have been removed # before option processing query = (args + coords).join(" ") if query == "" out << "Please specify a location (run `geocode -h` for more info).\n" exit 1 end if show_url and show_json out << "You can only specify one of -j and -u.\n" exit 2 end if show_url q = Geocoder::Query.new(query) out << q.url + "\n" exit 0 end if show_json q = Geocoder::Query.new(query) out << q.lookup.send(:fetch_raw_data, q) + "\n" exit 0 end if (result = Geocoder.search(query).first) nominatim = Geocoder::Lookup.get(:nominatim) lines = [ ["Latitude", result.latitude], ["Longitude", result.longitude], ["Full address", result.address], ["City", result.city], ["State/province", result.state], ["Postal code", result.postal_code], ["Country", result.country], ["Map", nominatim.map_link_url(result.coordinates)], ] lines.each do |line| out << (line[0] + ": ").ljust(18) + line[1].to_s + "\n" end exit 0 else out << "Location '#{query}' not found.\n" exit 1 end end end end ����������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/kernel_logger.rb��������������������������������������������������������0000644�0000041�0000041�00000001107�13503210114�021023� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������module Geocoder class KernelLogger include Singleton def add(level, message) return unless log_message_at_level?(level) case level when ::Logger::DEBUG, ::Logger::INFO puts message when ::Logger::WARN warn message when ::Logger::ERROR raise message when ::Logger::FATAL fail message end end private # ---------------------------------------------------------------- def log_message_at_level?(level) level >= Geocoder.config.kernel_logger_level end end end ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/geocoder/logger.rb���������������������������������������������������������������0000644�0000041�0000041�00000002144�13503210114�017465� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'logger' module Geocoder def self.log(level, message) Logger.instance.log(level, message) end class Logger include Singleton SEVERITY = { debug: ::Logger::DEBUG, info: ::Logger::INFO, warn: ::Logger::WARN, error: ::Logger::ERROR, fatal: ::Logger::FATAL } def log(level, message) unless valid_level?(level) raise StandardError, "Geocoder tried to log a message with an invalid log level." end if current_logger.respond_to? :add current_logger.add(SEVERITY[level], message) else raise Geocoder::ConfigurationError, "Please specify valid logger for Geocoder. " + "Logger specified must be :kernel or must respond to `add(level, message)`." end nil end private # ---------------------------------------------------------------- def current_logger logger = Geocoder.config[:logger] if logger == :kernel logger = Geocoder::KernelLogger.instance end logger end def valid_level?(level) SEVERITY.keys.include?(level) end end end ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/hash_recursive_merge.rb����������������������������������������������������������0000644�0000041�0000041�00000004740�13503210114�020614� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # = Hash Recursive Merge # # Merges a Ruby Hash recursively, Also known as deep merge. # Recursive version of Hash#merge and Hash#merge!. # # Category:: Ruby # Package:: Hash # Author:: Simone Carletti <weppos@weppos.net> # Copyright:: 2007-2008 The Authors # License:: MIT License # Link:: http://www.simonecarletti.com/ # Source:: http://gist.github.com/gists/6391/ # module HashRecursiveMerge # # Recursive version of Hash#merge! # # Adds the contents of +other_hash+ to +hsh+, # merging entries in +hsh+ with duplicate keys with those from +other_hash+. # # Compared with Hash#merge!, this method supports nested hashes. # When both +hsh+ and +other_hash+ contains an entry with the same key, # it merges and returns the values from both arrays. # # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}} # h2 = {"b" => 254, "c" => {"c1" => 16, "c3" => 94}} # h1.rmerge!(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}} # # Simply using Hash#merge! would return # # h1.merge!(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}} # def rmerge!(other_hash) merge!(other_hash) do |key, oldval, newval| oldval.class == self.class ? oldval.rmerge!(newval) : newval end end # # Recursive version of Hash#merge # # Compared with Hash#merge!, this method supports nested hashes. # When both +hsh+ and +other_hash+ contains an entry with the same key, # it merges and returns the values from both arrays. # # Compared with Hash#merge, this method provides a different approch # for merging nasted hashes. # If the value of a given key is an Hash and both +other_hash+ abd +hsh # includes the same key, the value is merged instead replaced with # +other_hash+ value. # # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}} # h2 = {"b" => 254, "c" => {"c1" => 16, "c3" => 94}} # h1.rmerge(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}} # # Simply using Hash#merge would return # # h1.merge(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}} # def rmerge(other_hash) r = {} merge(other_hash) do |key, oldval, newval| r[key] = oldval.class == self.class ? oldval.rmerge(newval) : newval end end end class Hash include HashRecursiveMerge end ��������������������������������geocoder-1.5.1/lib/maxmind_database.rb��������������������������������������������������������������0000644�0000041�0000041�00000006550�13503210114�017705� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'csv' require 'net/http' module Geocoder module MaxmindDatabase extend self def download(package, dir = "tmp") filepath = File.expand_path(File.join(dir, archive_filename(package))) open(filepath, 'wb') do |file| uri = URI.parse(archive_url(package)) Net::HTTP.start(uri.host, uri.port) do |http| http.request_get(uri.path) do |resp| # TODO: show progress resp.read_body do |segment| file.write(segment) end end end end end def insert(package, dir = "tmp") data_files(package, dir).each do |filepath,table| print "Resetting table #{table}..." ActiveRecord::Base.connection.execute("DELETE FROM #{table}") puts "done" insert_into_table(table, filepath) end end def archive_filename(package) p = archive_url_path(package) s = !(pos = p.rindex('/')).nil? && pos + 1 || 0 p[s..-1] end private # ------------------------------------------------------------- def table_columns(table_name) { maxmind_geolite_city_blocks: %w[start_ip_num end_ip_num loc_id], maxmind_geolite_city_location: %w[loc_id country region city postal_code latitude longitude metro_code area_code], maxmind_geolite_country: %w[start_ip end_ip start_ip_num end_ip_num country_code country] }[table_name.to_sym] end def insert_into_table(table, filepath) start_time = Time.now print "Loading data for table #{table}" rows = [] columns = table_columns(table) CSV.foreach(filepath, encoding: "ISO-8859-1") do |line| # Some files have header rows. # skip if starts with "Copyright" or "locId" or "startIpNum" next if line.first.match(/[A-z]/) rows << line.to_a if rows.size == 10000 insert_rows(table, columns, rows) rows = [] print "." end end insert_rows(table, columns, rows) if rows.size > 0 puts "done (#{Time.now - start_time} seconds)" end def insert_rows(table, headers, rows) value_strings = rows.map do |row| "(" + row.map{ |col| sql_escaped_value(col) }.join(',') + ")" end q = "INSERT INTO #{table} (#{headers.join(',')}) " + "VALUES #{value_strings.join(',')}" ActiveRecord::Base.connection.execute(q) end def sql_escaped_value(value) value.to_i.to_s == value ? value : ActiveRecord::Base.connection.quote(value) end def data_files(package, dir = "tmp") case package when :geolite_city_csv # use the last two in case multiple versions exist files = Dir.glob(File.join(dir, "GeoLiteCity_*/*.csv"))[-2..-1].sort Hash[*files.zip(["maxmind_geolite_city_blocks", "maxmind_geolite_city_location"]).flatten] when :geolite_country_csv {File.join(dir, "GeoIPCountryWhois.csv") => "maxmind_geolite_country"} end end def archive_url(package) base_url + archive_url_path(package) end def archive_url_path(package) { geolite_country_csv: "GeoIPCountryCSV.zip", geolite_city_csv: "GeoLiteCity_CSV/GeoLiteCity-latest.zip", geolite_asn_csv: "asnum/GeoIPASNum2.zip" }[package] end def base_url "http://geolite.maxmind.com/download/geoip/database/" end end end ��������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/generators/����������������������������������������������������������������������0000755�0000041�0000041�00000000000�13503210114�016242� 5����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/generators/geocoder/�������������������������������������������������������������0000755�0000041�0000041�00000000000�13503210114�020031� 5����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/generators/geocoder/maxmind/�����������������������������������������������������0000755�0000041�0000041�00000000000�13503210114�021466� 5����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/generators/geocoder/maxmind/geolite_city_generator.rb����������������������������0000644�0000041�0000041�00000001715�13503210114�026545� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'rails/generators/migration' require 'generators/geocoder/migration_version' module Geocoder module Generators module Maxmind class GeoliteCityGenerator < Rails::Generators::Base include Rails::Generators::Migration include Generators::MigrationVersion source_root File.expand_path('../templates', __FILE__) def copy_migration_files migration_template "migration/geolite_city.rb", "db/migrate/geocoder_maxmind_geolite_city.rb" end # Define the next_migration_number method (necessary for the # migration_template method to work) def self.next_migration_number(dirname) if ActiveRecord::Base.timestamped_migrations sleep 1 # make sure each time we get a different timestamp Time.new.utc.strftime("%Y%m%d%H%M%S") else "%.3d" % (current_migration_number(dirname) + 1) end end end end end end ���������������������������������������������������geocoder-1.5.1/lib/generators/geocoder/maxmind/geolite_country_generator.rb�������������������������0000644�0000041�0000041�00000001726�13503210114�027302� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'rails/generators/migration' require 'generators/geocoder/migration_version' module Geocoder module Generators module Maxmind class GeoliteCountryGenerator < Rails::Generators::Base include Rails::Generators::Migration include Generators::MigrationVersion source_root File.expand_path('../templates', __FILE__) def copy_migration_files migration_template "migration/geolite_country.rb", "db/migrate/geocoder_maxmind_geolite_country.rb" end # Define the next_migration_number method (necessary for the # migration_template method to work) def self.next_migration_number(dirname) if ActiveRecord::Base.timestamped_migrations sleep 1 # make sure each time we get a different timestamp Time.new.utc.strftime("%Y%m%d%H%M%S") else "%.3d" % (current_migration_number(dirname) + 1) end end end end end end ������������������������������������������geocoder-1.5.1/lib/generators/geocoder/maxmind/templates/�������������������������������������������0000755�0000041�0000041�00000000000�13503210114�023464� 5����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/generators/geocoder/maxmind/templates/migration/���������������������������������0000755�0000041�0000041�00000000000�13503210114�025455� 5����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb������������������0000644�0000041�0000041�00000002162�13503210114�030463� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������class GeocoderMaxmindGeoliteCity < ActiveRecord::Migration<%= migration_version %> def self.up create_table :maxmind_geolite_city_blocks, id: false do |t| t.column :start_ip_num, :bigint, null: false t.column :end_ip_num, :bigint, null: false t.column :loc_id, :bigint, null: false end add_index :maxmind_geolite_city_blocks, :loc_id add_index :maxmind_geolite_city_blocks, :start_ip_num, unique: true add_index :maxmind_geolite_city_blocks, [:end_ip_num, :start_ip_num], unique: true, name: 'index_maxmind_geolite_city_blocks_on_end_ip_num_range' create_table :maxmind_geolite_city_location, id: false do |t| t.column :loc_id, :bigint, null: false t.string :country, null: false t.string :region, null: false t.string :city t.string :postal_code, null: false t.float :latitude t.float :longitude t.integer :metro_code t.integer :area_code end add_index :maxmind_geolite_city_location, :loc_id, unique: true end def self.down drop_table :maxmind_geolite_city_location drop_table :maxmind_geolite_city_blocks end end ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb���������������0000644�0000041�0000041�00000001074�13503210114�031217� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������class GeocoderMaxmindGeoliteCountry < ActiveRecord::Migration<%= migration_version %> def self.up create_table :maxmind_geolite_country, id: false do |t| t.column :start_ip, :string t.column :end_ip, :string t.column :start_ip_num, :bigint, null: false t.column :end_ip_num, :bigint, null: false t.column :country_code, :string, null: false t.column :country, :string, null: false end add_index :maxmind_geolite_country, :start_ip_num, unique: true end def self.down drop_table :maxmind_geolite_country end end ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/generators/geocoder/migration_version.rb�����������������������������������������0000644�0000041�0000041�00000000431�13503210114�024112� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������module Geocoder module Generators module MigrationVersion def rails_5? Rails::VERSION::MAJOR == 5 end def migration_version if rails_5? "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" end end end end end ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/generators/geocoder/config/������������������������������������������������������0000755�0000041�0000041�00000000000�13503210114�021276� 5����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/generators/geocoder/config/templates/��������������������������������������������0000755�0000041�0000041�00000000000�13503210114�023274� 5����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/generators/geocoder/config/templates/initializer.rb������������������������������0000644�0000041�0000041�00000002107�13503210114�026144� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Geocoder.configure( # Geocoding options # timeout: 3, # geocoding service timeout (secs) # lookup: :nominatim, # name of geocoding service (symbol) # ip_lookup: :ipinfo_io, # name of IP address geocoding service (symbol) # language: :en, # ISO-639 language code # use_https: false, # use HTTPS for lookup requests? (if supported) # http_proxy: nil, # HTTP proxy server (user:pass@host:port) # https_proxy: nil, # HTTPS proxy server (user:pass@host:port) # api_key: nil, # API key for geocoding service # cache: nil, # cache object (must respond to #[], #[]=, and #del) # cache_prefix: 'geocoder:', # prefix (string) to use for all cache keys # Exceptions that should not be rescued by default # (if you want to implement custom error handling); # supports SocketError and Timeout::Error # always_raise: [], # Calculation options # units: :mi, # :km for kilometers or :mi for miles # distances: :linear # :spherical or :linear ) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������geocoder-1.5.1/lib/generators/geocoder/config/config_generator.rb�����������������������������������0000644�0000041�0000041�00000000631�13503210114�025136� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'rails/generators' module Geocoder class ConfigGenerator < Rails::Generators::Base source_root File.expand_path("../templates", __FILE__) desc "This generator creates an initializer file at config/initializers, " + "with the default configuration options for Geocoder." def add_initializer template "initializer.rb", "config/initializers/geocoder.rb" end end end �������������������������������������������������������������������������������������������������������geocoder-1.5.1/geocoder.gemspec���������������������������������������������������������������������0000644�0000041�0000041�00000016501�13503210114�016462� 0����������������������������������������������������������������������������������������������������ustar �www-data������������������������www-data���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: geocoder 1.5.1 ruby lib Gem::Specification.new do |s| s.name = "geocoder".freeze s.version = "1.5.1" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.metadata = { "changelog_uri" => "https://github.com/alexreisner/geocoder/blob/master/CHANGELOG.md", "source_code_uri" => "https://github.com/alexreisner/geocoder" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Alex Reisner".freeze] s.date = "2019-01-23" s.description = "Provides object geocoding (by street or IP address), reverse geocoding (coordinates to street address), distance queries for ActiveRecord and Mongoid, result caching, and more. Designed for Rails but works with Sinatra and other Rack frameworks too.".freeze s.email = ["alex@alexreisner.com".freeze] s.executables = ["geocode".freeze] s.files = ["CHANGELOG.md".freeze, "LICENSE".freeze, "README.md".freeze, "bin/geocode".freeze, "examples/autoexpire_cache_dalli.rb".freeze, "examples/autoexpire_cache_redis.rb".freeze, "examples/cache_bypass.rb".freeze, "examples/reverse_geocode_job.rb".freeze, "lib/generators/geocoder/config/config_generator.rb".freeze, "lib/generators/geocoder/config/templates/initializer.rb".freeze, "lib/generators/geocoder/maxmind/geolite_city_generator.rb".freeze, "lib/generators/geocoder/maxmind/geolite_country_generator.rb".freeze, "lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb".freeze, "lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb".freeze, "lib/generators/geocoder/migration_version.rb".freeze, "lib/geocoder.rb".freeze, "lib/geocoder/cache.rb".freeze, "lib/geocoder/calculations.rb".freeze, "lib/geocoder/cli.rb".freeze, "lib/geocoder/configuration.rb".freeze, "lib/geocoder/configuration_hash.rb".freeze, "lib/geocoder/esri_token.rb".freeze, "lib/geocoder/exceptions.rb".freeze, "lib/geocoder/ip_address.rb".freeze, "lib/geocoder/kernel_logger.rb".freeze, "lib/geocoder/logger.rb".freeze, "lib/geocoder/lookup.rb".freeze, "lib/geocoder/lookups/amap.rb".freeze, "lib/geocoder/lookups/baidu.rb".freeze, "lib/geocoder/lookups/baidu_ip.rb".freeze, "lib/geocoder/lookups/ban_data_gouv_fr.rb".freeze, "lib/geocoder/lookups/base.rb".freeze, "lib/geocoder/lookups/bing.rb".freeze, "lib/geocoder/lookups/db_ip_com.rb".freeze, "lib/geocoder/lookups/dstk.rb".freeze, "lib/geocoder/lookups/esri.rb".freeze, "lib/geocoder/lookups/freegeoip.rb".freeze, "lib/geocoder/lookups/geocoder_ca.rb".freeze, "lib/geocoder/lookups/geocoder_us.rb".freeze, "lib/geocoder/lookups/geocodio.rb".freeze, "lib/geocoder/lookups/geoip2.rb".freeze, "lib/geocoder/lookups/geoportail_lu.rb".freeze, "lib/geocoder/lookups/google.rb".freeze, "lib/geocoder/lookups/google_places_details.rb".freeze, "lib/geocoder/lookups/google_places_search.rb".freeze, "lib/geocoder/lookups/google_premier.rb".freeze, "lib/geocoder/lookups/here.rb".freeze, "lib/geocoder/lookups/ip2location.rb".freeze, "lib/geocoder/lookups/ipapi_com.rb".freeze, "lib/geocoder/lookups/ipdata_co.rb".freeze, "lib/geocoder/lookups/ipinfo_io.rb".freeze, "lib/geocoder/lookups/ipstack.rb".freeze, "lib/geocoder/lookups/latlon.rb".freeze, "lib/geocoder/lookups/location_iq.rb".freeze, "lib/geocoder/lookups/mapbox.rb".freeze, "lib/geocoder/lookups/mapquest.rb".freeze, "lib/geocoder/lookups/maxmind.rb".freeze, "lib/geocoder/lookups/maxmind_geoip2.rb".freeze, "lib/geocoder/lookups/maxmind_local.rb".freeze, "lib/geocoder/lookups/nominatim.rb".freeze, "lib/geocoder/lookups/opencagedata.rb".freeze, "lib/geocoder/lookups/pelias.rb".freeze, "lib/geocoder/lookups/pickpoint.rb".freeze, "lib/geocoder/lookups/pointpin.rb".freeze, "lib/geocoder/lookups/postcode_anywhere_uk.rb".freeze, "lib/geocoder/lookups/postcodes_io.rb".freeze, "lib/geocoder/lookups/smarty_streets.rb".freeze, "lib/geocoder/lookups/telize.rb".freeze, "lib/geocoder/lookups/tencent.rb".freeze, "lib/geocoder/lookups/test.rb".freeze, "lib/geocoder/lookups/yandex.rb".freeze, "lib/geocoder/models/active_record.rb".freeze, "lib/geocoder/models/base.rb".freeze, "lib/geocoder/models/mongo_base.rb".freeze, "lib/geocoder/models/mongo_mapper.rb".freeze, "lib/geocoder/models/mongoid.rb".freeze, "lib/geocoder/query.rb".freeze, "lib/geocoder/railtie.rb".freeze, "lib/geocoder/request.rb".freeze, "lib/geocoder/results/amap.rb".freeze, "lib/geocoder/results/baidu.rb".freeze, "lib/geocoder/results/baidu_ip.rb".freeze, "lib/geocoder/results/ban_data_gouv_fr.rb".freeze, "lib/geocoder/results/base.rb".freeze, "lib/geocoder/results/bing.rb".freeze, "lib/geocoder/results/db_ip_com.rb".freeze, "lib/geocoder/results/dstk.rb".freeze, "lib/geocoder/results/esri.rb".freeze, "lib/geocoder/results/freegeoip.rb".freeze, "lib/geocoder/results/geocoder_ca.rb".freeze, "lib/geocoder/results/geocoder_us.rb".freeze, "lib/geocoder/results/geocodio.rb".freeze, "lib/geocoder/results/geoip2.rb".freeze, "lib/geocoder/results/geoportail_lu.rb".freeze, "lib/geocoder/results/google.rb".freeze, "lib/geocoder/results/google_places_details.rb".freeze, "lib/geocoder/results/google_places_search.rb".freeze, "lib/geocoder/results/google_premier.rb".freeze, "lib/geocoder/results/here.rb".freeze, "lib/geocoder/results/ip2location.rb".freeze, "lib/geocoder/results/ipapi_com.rb".freeze, "lib/geocoder/results/ipdata_co.rb".freeze, "lib/geocoder/results/ipinfo_io.rb".freeze, "lib/geocoder/results/ipstack.rb".freeze, "lib/geocoder/results/latlon.rb".freeze, "lib/geocoder/results/location_iq.rb".freeze, "lib/geocoder/results/mapbox.rb".freeze, "lib/geocoder/results/mapquest.rb".freeze, "lib/geocoder/results/maxmind.rb".freeze, "lib/geocoder/results/maxmind_geoip2.rb".freeze, "lib/geocoder/results/maxmind_local.rb".freeze, "lib/geocoder/results/nominatim.rb".freeze, "lib/geocoder/results/opencagedata.rb".freeze, "lib/geocoder/results/pelias.rb".freeze, "lib/geocoder/results/pickpoint.rb".freeze, "lib/geocoder/results/pointpin.rb".freeze, "lib/geocoder/results/postcode_anywhere_uk.rb".freeze, "lib/geocoder/results/postcodes_io.rb".freeze, "lib/geocoder/results/smarty_streets.rb".freeze, "lib/geocoder/results/telize.rb".freeze, "lib/geocoder/results/tencent.rb".freeze, "lib/geocoder/results/test.rb".freeze, "lib/geocoder/results/yandex.rb".freeze, "lib/geocoder/sql.rb".freeze, "lib/geocoder/stores/active_record.rb".freeze, "lib/geocoder/stores/base.rb".freeze, "lib/geocoder/stores/mongo_base.rb".freeze, "lib/geocoder/stores/mongo_mapper.rb".freeze, "lib/geocoder/stores/mongoid.rb".freeze, "lib/geocoder/version.rb".freeze, "lib/hash_recursive_merge.rb".freeze, "lib/maxmind_database.rb".freeze, "lib/tasks/geocoder.rake".freeze, "lib/tasks/maxmind.rake".freeze] s.homepage = "http://www.rubygeocoder.com".freeze s.licenses = ["MIT".freeze] s.post_install_message = "\n\nNOTE: Geocoder's default IP address lookup has changed from FreeGeoIP.net to IPInfo.io. If you explicitly specify :freegeoip in your configuration you must choose a different IP lookup before FreeGeoIP is discontinued on July 1, 2018. If you do not explicitly specify :freegeoip you do not need to change anything.\n\n".freeze s.required_ruby_version = Gem::Requirement.new(">= 2.0.0".freeze) s.rubygems_version = "2.5.2.1".freeze s.summary = "Complete geocoding solution for Ruby.".freeze end �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������