Add Google Map of voters
[selectricity-live] / vendor / plugins / geokit / README
diff --git a/vendor/plugins/geokit/README b/vendor/plugins/geokit/README
new file mode 100644 (file)
index 0000000..3e72859
--- /dev/null
@@ -0,0 +1,445 @@
+## FEATURE SUMMARY
+
+This plugin provides key functionality for location-oriented Rails applications:
+
+- Distance calculations, for both flat and spherical environments. For example, 
+  given the location of two points on the earth, you can calculate the miles/KM 
+  between them.
+- ActiveRecord distance-based finders. For example, you can find all the points 
+  in your database within a 50-mile radius.
+- Geocoding from multiple providers. It currently supports Google, Yahoo, 
+  Geocoder.us, and Geocoder.ca geocoders, and it provides a uniform response 
+  structure from all of them. It also provides a fail-over mechanism, in case 
+  your input fails to geocode in one service.
+- IP-based location lookup utilizing hostip.info. Provide an IP address, and get
+  city name and latitude/longitude in return
+- A before_filter helper to geocoder the user's location based on IP address, 
+  and retain the location in a cookie.
+  
+The goal of this plugin is to provide the common functionality for location-oriented 
+applications (geocoding, location lookup, distance calculation) in an easy-to-use 
+package.
+
+## A NOTE ON TERMINOLOGY
+
+Throughout the code and API of this, latitude and longitude are referred to as lat 
+and lng.  We've found over the long term the abbreviation saves lots of typing time.
+
+## DISTANCE CALCULATIONS AND QUERIES
+
+If you want only distance calculation services, you need only mix in the Mappable 
+module like so:
+
+    class Location
+       include GeoKit::Mappable
+    end
+
+After doing so, you can do things like:
+
+    Location.distance_between(from, to) 
+
+with optional parameters :units and :formula.  Values for :units can be :miles or 
+:kms with :miles as the default.  Values for :formula can be :sphere or :flat with
+:sphere as the default.  :sphere gives you Haversine calculations, while :flat 
+gives the Pythagoreum Theory.  These defaults persist through out the plug-in.
+
+You can also do:
+
+    location.distance_to(other)
+
+The real power and utility of the plug-in is in its query support.  This is 
+achieved through mixing into an ActiveRecord model object:
+
+    class Location < ActiveRecord::Base
+       acts_as_mappable
+    end
+
+The plug-in uses the above-mentioned defaults, but can be modified to use 
+different units and a different formulae.  This is done through the :default_units
+and :default_formula keys which accept the same values as mentioned above.
+
+The plug-in creates a calculated column and potentially a calculated condition.  
+By default, these are known as "distance" but this can be changed through the
+:distance_field_name key.  
+
+So, an alternative invocation would look as below:
+
+    class Location < ActiveRecord::Base
+       acts_as_mappable :default_units => :kms, 
+                        :default_formula => :flat, 
+                        :distance_field_name => :distance
+    end
+
+You can also define alternative column names for latitude and longitude using
+the :lat_column_name and :lng_column_name keys.  The defaults are 'lat' and
+'lng' respectively.
+
+Thereafter, a set of finder methods are made available.  Below are the 
+different combinations:
+
+Origin as a two-element array of latititude/longitude:
+
+               find(:all, :origin => [37.792,-122.393])
+
+Origin as a geocodeable string:
+
+               find(:all, :origin => '100 Spear st, San Francisco, CA')
+
+Origin as an object which responds to lat and lng methods, 
+or latitude and longitude methods, or whatever methods you have 
+specified for lng_column_name and lat_column_name:
+
+               find(:all, :origin=>my_store) # my_store.lat and my_store.lng methods exist
+
+Often you will need to find within a certain distance. The prefered syntax is:
+
+    find(:all, :origin => @somewhere, :within => 5)
+    
+. . . however these syntaxes will also work:
+
+    find_within(5, :origin => @somewhere)
+    find(:all, :origin => @somewhere, :conditions => "distance < 5")
+    
+Note however that the third form should be avoided. With either of the first two,
+GeoKit automatically adds a bounding box to speed up the radial query in the database.
+With the third form, it does not.
+
+If you need to combine distance conditions with other conditions, you should do
+so like this:
+
+    find(:all, :origin => @somewhere, :within => 5, :conditions=>['state=?',state])
+
+If :origin is not provided in the finder call, the find method 
+works as normal.  Further, the key is removed
+from the :options hash prior to invoking the superclass behavior.
+
+Other convenience methods work intuitively and are as follows:
+
+    find_within(distance, :origin => @somewhere)
+    find_beyond(distance, :origin => @somewhere)
+    find_closest(:origin => @somewhere)
+    find_farthest(:origin => @somewhere)
+
+where the options respect the defaults, but can be overridden if 
+desired.  
+
+Lastly, if all that is desired is the raw SQL for distance 
+calculations, you can use the following:
+
+    distance_sql(origin, units=default_units, formula=default_formula)
+
+Thereafter, you are free to use it in find_by_sql as you wish.
+
+There are methods available to enable you to get the count based upon
+the find condition that you have provided.  These all work similarly to
+the finders.  So for instance:
+
+    count(:origin, :conditions => "distance < 5")
+    count_within(distance, :origin => @somewhere)
+    count_beyond(distance, :origin => @somewhere)
+
+## FINDING WITHIN A BOUNDING BOX
+If you are displaying points on a map, you probably need to query for whatever falls within the rectangular bounds of the map:
+
+    Store.find :all, :bounds=>[sw_point,ne_point]
+
+The input to :bounds can be array with the two points or a Bounds object. However you provide them, the order should always be the southwest corner, northeast corner of the rectangle. Typically, you will be getting the sw_point and ne_point from a map that is displayed on a web page.
+
+If you need to calculate the bounding box from a point and radius, you can do that:
+
+    bounds=Bounds.from_point_and_radius(home,5)
+    Store.find :all, :bounds=>bounds
+
+## USING INCLUDES
+
+You can use includes along with your distance finders:
+
+    stores=Store.find :all, :origin=>home, :include=>[:reviews,:cities] :within=>5, :order=>'distance'
+
+*However*, ActiveRecord drops the calculated distance column when you use include. So, if you need to
+use the distance column, you'll have to re-calculate it post-query in Ruby:
+
+    stores.sort_by_distance_from(home)
+
+In this case, you may want to just use the bounding box
+condition alone in your SQL (there's no use calculating the distance twice):
+
+    bounds=Bounds.from_point_and_radius(home,5)
+    stores=Store.find :all, :include=>[:reviews,:cities] :bounds=>bounds
+    stores.sort_by_distance_from(home)
+
+## IP GEOCODING
+
+You can obtain the location for an IP at any time using the geocoder
+as in the following example:
+
+    location = IpGeocoder.geocode('12.215.42.19')
+
+where Location is a GeoLoc instance containing the latitude, 
+longitude, city, state, and country code.  Also, the success 
+value is true.
+
+If the IP cannot be geocoded, a GeoLoc instance is returned with a
+success value of false.  
+
+It should be noted that the IP address needs to be visible to the
+Rails application.  In other words, you need to ensure that the 
+requesting IP address is forwarded by any front-end servers that
+are out in front of the Rails app.  Otherwise, the IP will always
+be that of the front-end server.
+
+## IP GEOCODING HELPER
+
+A class method called geocode_ip_address has been mixed into the 
+ActionController::Base.  This enables before_filter style lookup of
+the IP address.  Since it is a filter, it can accept any of the 
+available filter options.
+
+Usage is as below:
+
+    class LocationAwareController < ActionController::Base
+      geocode_ip_address
+    end
+
+A first-time lookup will result in the GeoLoc class being stored
+in the session as :geo_location as well as in a cookie called
+:geo_session.  Subsequent lookups will use the session value if it
+exists or the cookie value if it doesn't exist.  The last resort is
+to make a call to the web service.  Clients are free to manage the
+cookie as they wish.
+
+The intent of this feature is to be able to provide a good guess as
+to a new visitor's location.
+
+## INTEGRATED FIND AND GEOCODING
+
+Geocoding has been integrated with the finders enabling you to pass
+a physical address or an IP address.  This would look the following:
+
+    Location.find_farthest(:origin => '217.15.10.9')
+    Location.find_farthest(:origin => 'Irving, TX')
+
+where the IP or physical address would be geocoded to a location and
+then the resulting latitude and longitude coordinates would be used 
+in the find.  This is not expected to be common usage, but it can be
+done nevertheless.
+
+## ADDRESS GEOCODING
+
+GeoKit can geocode addresses using multiple geocodeing web services.
+Currently, GeoKit supports Google, Yahoo, and Geocoder.us geocoding 
+services. 
+
+These geocoder services are made available through three classes: 
+GoogleGeocoder, YahooGeocoder, and UsGeocoder.  Further, an additional
+geocoder class called MultiGeocoder incorporates an ordered failover
+sequence to increase the probability of successful geocoding.
+
+All classes are called using the following signature:
+
+    include GeoKit::Geocoders
+    location = XxxGeocoder.geocode(address)
+
+where you replace Xxx Geocoder with the appropriate class.  A GeoLoc
+instance is the result of the call.  This class has a "success"
+attribute which will be true if a successful geocoding occurred.  
+If successful, the lat and lng properties will be populated.
+
+Geocoders are named with the naming convention NameGeocoder.  This
+naming convention enables Geocoder to auto-detect its sub-classes
+in order to create methods called name_geocoder(address) so that
+all geocoders are called through the base class.  This is done 
+purely for convenience; the individual geocoder classes are expected
+to be used independently.
+
+The MultiGeocoder class requires the configuration of a provider
+order which dictates what order to use the various geocoders.  Ordering
+is done through the PROVIDER_ORDER constant found in environment.rb.
+
+On installation, this plugin appends a template for your API keys to 
+your environment.rb. 
+
+Make sure your failover configuration matches the usage characteristics 
+of your application -- for example, if you routinely get bogus input to 
+geocode, your code will be much slower if you have to failover among 
+multiple geocoders before determining that the input was in fact bogus. 
+
+The Geocoder.geocode method returns a GeoLoc object. Basic usage:
+
+    loc=Geocoder.geocode('100 Spear St, San Francisco, CA')
+    if loc.success
+      puts loc.lat
+      puts loc.lng
+      puts loc.full_address
+    end
+
+## INTEGRATED FIND WITH ADDRESS GEOCODING
+
+Just has you can pass an IP address directly into an ActiveRecord finder
+as the origin, you can also pass a physical address as the origin:
+
+    Location.find_closest(:origin => '100 Spear st, San Francisco, CA')
+
+where the physical address would be geocoded to a location and then the 
+resulting latitude and longitude coordinates would be used in the 
+find. 
+
+Note that if the address fails to geocode, the find method will raise an 
+ActiveRecord::GeocodeError you must be prepared to catch. Alternatively,
+You can geocoder the address beforehand, and pass the resulting lat/lng
+into the finder if successful.
+
+## Auto Geocoding
+
+If your geocoding needs are simple, you can tell your model to automatically
+geocode itself on create:
+
+    class Store < ActiveRecord::Base
+      acts_as_mappable :auto_geocode=>true
+    end
+
+It takes two optional params:
+
+    class Store < ActiveRecord::Base
+      acts_as_mappable :auto_geocode=>{:field=>:address, :error_message=>'Could not geocode address'}
+    end
+
+. . . which is equivilent to:
+
+    class Store << ActiveRecord::Base
+      acts_as_mappable
+      before_validation_on_create :geocode_address
+
+      private
+      def geocode_address
+        geo=GeoKit::Geocoders::MultiGeocoder.geocode (address)
+        errors.add(:address, "Could not Geocode address") if !geo.success
+        self.lat, self.lng = geo.lat,geo.lng if geo.success
+      end
+    end
+    
+If you need any more complicated geocoding behavior for your model, you should roll your own 
+before_validate callback.
+
+
+## Distances, headings, endpoints, and midpoints
+
+    distance=home.distance_from(work, :units=>:miles)
+    heading=home.heading_to(work) # result is in degrees, 0 is north
+    endpoint=home.endpoint(90,2)  # two miles due east
+    midpoing=home.midpoint_to(work)
+
+## Cool stuff you can do with bounds
+    
+    bounds=Bounds.new(sw_point,ne_point)
+    bounds.contains?(home)
+    puts bounds.center
+    
+
+HOW TO . . .
+=================================================================================
+
+## How to install the GeoKit plugin 
+    cd [APP_ROOT]
+    ruby script/plugin install svn://rubyforge.org/var/svn/geokit/trunk
+      or, to install as an external (your project must be version controlled):
+    ruby script/plugin install -x svn://rubyforge.org/var/svn/geokit/trunk
+
+## How to find all stores within a 10-mile radius of a given lat/lng
+1. ensure your stores table has lat and lng columns with numeric or float 
+   datatypes to store your latitude/longitude
+
+3. use acts_as_mappable on your store model:
+    class Store < ActiveRecord::Base
+       acts_as_mappable
+       ...
+    end
+3. finders now have extra capabilities:
+    Store.find(:all, :origin =>[32.951613,-96.958444], :within=>10)
+
+## How to geocode an address
+
+1. configure your geocoder key(s) in environment.rb
+
+2. also in environment.rb, make sure that PROVIDER_ORDER reflects the 
+   geocoder(s). If you only want to use one geocoder, there should
+   be only one symbol in the array. For example:
+    PROVIDER_ORDER=[:google]
+   
+3. Test it out in script/console
+    include GeoKit::Geocoders
+    res = MultiGeocoder.geocode('100 Spear St, San Francisco, CA')
+    puts res.lat
+    puts res.lng
+    puts res.full_address 
+    ... etc. The return type is GeoLoc, see the API for 
+    all the methods you can call on it.
+
+## How to find all stores within 10 miles of a given address
+
+1. as above, ensure your table has the lat/lng columns, and you've
+   applied acts_as_mappable to the Store model.
+
+2. configure and test out your geocoder, as above
+
+3. pass the address in under the :origin key
+               Store.find(:all, :origin=>'100 Spear st, San Francisco, CA', 
+                          :within=>10)
+
+4. you can also use a zipcode, or anything else that's geocodable:
+               Store.find(:all, :origin=>'94117', 
+                          :conditions=>'distance<10')
+
+## How to sort a query by distance from an origin
+
+You now have access to a 'distance' column, and you can use it
+as you would any other column. For example:
+               Store.find(:all, :origin=>'94117', :order=>'distance')
+
+## How to elements of an array according to distance from a common point
+
+Usually, you can do your sorting in the database as part of your find call.
+If you need to sort things post-query, you can do so:
+
+    stores=Store.find :all
+    stores.sort_by_distance_from(home)
+    puts stores.first.distance
+    
+Obviously, each of the items in the array must have a latitude/longitude so
+they can be sorted by distance.
+
+
+HIGH-LEVEL NOTES ON WHAT'S WHERE
+=================================================================================
+
+acts_as_mappable.rb, as you'd expect, contains the ActsAsMappable
+module which gets mixed into your models to provide the 
+location-based finder goodness.
+
+mappable.rb contains the Mappable module, which provides basic
+distance calculation methods, i.e., calculating the distance
+between two points. 
+
+mappable.rb also contains LatLng, GeoLoc, and Bounds.
+LatLng is a simple container for latitude and longitude, but 
+it's made more powerful by mixing in the above-mentioned Mappable
+module -- therefore, you can calculate easily the distance between two
+LatLng ojbects with distance = first.distance_to(other)
+
+GeoLoc (also in mappable.rb) represents an address or location which
+has been geocoded. You can get the city, zipcode, street address, etc.
+from a GeoLoc object. GeoLoc extends LatLng, so you also get lat/lng
+AND the Mappable modeule goodness for free.
+
+geocoders.rb contains the geocoder classes.
+
+ip_geocode_lookup.rb contains the before_filter helper method which
+enables auto lookup of the requesting IP address.
+
+## IMPORTANT NOTE: We have appended to your environment.rb file
+
+Installation of this plugin has appended an API key template 
+to your environment.rb file. You *must* add your own keys for the various
+geocoding services if you want to use geocoding. If you need to refer to the original
+template again, see the api_keys_template file in the root of the plugin.

Benjamin Mako Hill || Want to submit a patch?