Jody Heavener
Jody Heavener

Reputation: 2864

Filter Rails records to within geo-polygonal area using latitude/longitude

In my Rails app I have search areas, which are geographic polygons (created using the Google Maps API). Once drawn on the map, the search area's coordinates are saved to a record as an array of hashes. It looks something like this:

SearchArea.last.geo
=> [{"lat"=>"48.425555463221066", "lng"=>"-123.3793830871582"}, {"lat"=>"48.41336528699442", "lng"=>"-123.37800979614258"}, {"lat"=>"48.41006090395106", "lng"=>"-123.31672668457031"}, {"lat"=>"48.44696663691928", "lng"=>"-123.32067489624023"}]

I also have a handful of location records whose coordinates are saved as a hash, like so:

Location.last.geo
=> {"lat"=>"28.8138124", "lng"=>"-96.9977682"}

I need to be able to search for/filter records that have geo coordinates that fall within a given search area. I have no experience with geospatial programming, so I'm hoping someone can lend a hand here. It would be really appreciated!

Things I've looked in to:

Upvotes: 1

Views: 1292

Answers (1)

Jody Heavener
Jody Heavener

Reputation: 2864

Nate suggested PostGis in the comments above, so I went with that.

I ended up using this tutorial to install PostGis on my Rails app, with some minor changes for 2016:

  • The line where it says to add require 'active_record/connection_adapters/postgis_adapter/railtie' should actually be require 'active_record/connection_adapters/postgis_adapter'
  • The syntax for creating a geo table columns has changed from t.point :latlon, :geographic => true to t.geography "latlon", limit: {:srid=>4326, :type=>"point", :geographic=>true}
  • Based on this issue I also added the following initializer:

    RGeo::ActiveRecord::SpatialFactoryStore.instance.tap do |config|
      config.default = RGeo::Geographic.spherical_factory(srid: 4326)
    end
    

The rest of the tutorial was a pretty good reference, from installing the gems to updating the database configuration.

From there I was then able to create a Location with a Point:

Location.last.geo = "POINT(-123.366 48.428)"

As well as my polygonal area to search within using a Polygon:

SearchArea.last.geo = "POLYGON((-123.382 48.426, -123.353 48.402, -123.314 48.423, -123.332 48.443, -123.382 48.426))"

Finally, the query; it was so simple!

sa = SearchArea.last
Location.where("ST_Intersects(geo, '#{sa.geo}')")

Thanks for the suggestion, Nate!

Upvotes: 3

Related Questions