PostGIS on Rails

37
PostGIS on Rails Matt Nemenman [email protected] @quarterdome 1 Friday, April 19,

description

How to use PostgreSQL PostGIS extension in Ruby on Rails environment.

Transcript of PostGIS on Rails

Page 1: PostGIS on Rails

PostGIS on Rails

Matt [email protected]

@quarterdome

1Friday, April 19,

Page 2: PostGIS on Rails

About me

Programmer

Love maps

Building map based rental search engine @ Apartment List

2Friday, April 19,

Page 3: PostGIS on Rails

Location Aware Apps

Where am I?

What is around me?

3Friday, April 19,

Page 4: PostGIS on Rails

Where am I?

Latitude and Longitude

HTML5 geolocation or GPS

Address

Geocoding

Reverse geocoding

4Friday, April 19,

Page 5: PostGIS on Rails

Where am I?

What if you need to know ...

Neighborhood

School district

National park

Earthquake safety zone

5Friday, April 19,

Page 6: PostGIS on Rails

What is around me?

Yelp and Google Places API

Restaurants, bars, etc.

Points of interest

6Friday, April 19,

Page 7: PostGIS on Rails

What is around me?

What if you want to know ...

What are three neighborhoods closest to me?

Average rent for 1 bedroom in Lower Pacific Heights

Crime rate on my city block

Who is around me?

7Friday, April 19,

Page 8: PostGIS on Rails

When 3rd party APIs fall short ...

Get your own data set

Build your own solution

8Friday, April 19,

Page 9: PostGIS on Rails

Spatial Systems

MongoDB

Solr

MySQL

Oracle / DB2

PostGIS

9Friday, April 19,

Page 10: PostGIS on Rails

PostGIS

Geospatial extension to Postgres

New datatypes

Functions to work with those datatypes

Spatial indices using GiST

create extension postgis;

10Friday, April 19,

Page 11: PostGIS on Rails

A Simple Example

11Friday, April 19,

Page 12: PostGIS on Rails

A simple example

Yosemite Park Ranger

Tracking bears equipped with GPS transmitters

Want to show all the bears on Google Maps

12Friday, April 19,

Page 13: PostGIS on Rails

Bears (database migration)

create_table :bears do |t| t.column :lat, :float t.column :lon, :floatend

add_index :bears, :latadd_index :bears, :lonadd_index :bears, [:lat, :lon]

13Friday, April 19,

Page 14: PostGIS on Rails

Bears (model)class Bear < ActiveRecord::Base

def self.bbox(sw_lon, sw_lat, ne_lon, ne_lat) self .where( :lon => sw_lon..ne_lon ) .where( :lat => sw_lat..ne_lat )end

end

14Friday, April 19,

Page 15: PostGIS on Rails

A PostGIS example (migration)

create_table :bears do |t| t.column :coordinates, :geometry, :srid => 4326end

add_index :bears, :coordinates, :spatial => true

15Friday, April 19,

Page 16: PostGIS on Rails

A PostGIS example (model)

def self.box(sw_lon, sw_lat, ne_lon, ne_lat)

factory = Rails.application.spatial_factory

sw = factory.point(sw_lon, sw_lat) nw = factory.point(sw_lon, ne_lat) ne = factory.point(ne_lon, ne_lat) se = factory.point(ne_lon, sw_lat)

ring = factory.linear_ring([sw, nw, ne, se]) bbox = factory.polygon(ring)

self .where('ST_Intersects(coordinates, :bbox)', :bbox => bbox)end

16Friday, April 19,

Page 17: PostGIS on Rails

A PostGIS example

1_000_000.times do Bear.create!( ...)

end

17Friday, April 19,

Page 18: PostGIS on Rails

1,000,000 bears

PostGIS is 1.5x to 50x faster

18Friday, April 19,

Page 19: PostGIS on Rails

Active Record PostGIS Adapter

Migration support

Automatic conversion of PostGIS datatypes to Ruby (RGeo) objects and back

gem 'pg'gem 'rgeo'gem 'activerecord-postgis-adapter'

19Friday, April 19,

Page 20: PostGIS on Rails

Rails Migrations

add_column :bears, :coordinates, :geometry, :srid => 4326

add_index :bears, :coordinates, :spatial => true

20Friday, April 19,

Page 21: PostGIS on Rails

Postgres Table

=> \d bears Table "public.bears" Column | Type -------------+----------------------------- id | integer coordinates | geometry(Geometry,4326) Indexes: "pins_pkey" PRIMARY KEY, btree (id) "index_pins_on_coordinates" gist (coordinates)

21Friday, April 19,

Page 22: PostGIS on Rails

PostGIS Data

=> select id, coordinates from bears limit 4; id | coordinates ----+---------------------------------------------------- 1 | 0101000020E61000002A8A632C341F664021805D8DDBEB4BC0 2 | 0101000020E61000004DF900A54A5866C0A2BAB6AC827D50C0 3 | 0101000020E61000002450EA628F5259C01C789C77C2883040 4 | 0101000020E610000038760C7B85443C4013206005DC2C48C0(4 rows)

22Friday, April 19,

Page 23: PostGIS on Rails

RGeo

##> bear=Bear.first => #<Bear id: 1, coordinates: #<RGeo::Geos::CAPIPointImpl:0x3fd52ab501b4 "POINT (176.9751188224921 -55.84263770165877)">>

##> bear.coordinates.x => 176.9751188224921

##> bear.coordinates.y => -55.84263770165877

23Friday, April 19,

Page 24: PostGIS on Rails

More examples# \d parks Column | Type ------------------------+----------------------------- id | integer name | character varying(255) boundary | geometry(Geometry,4326)

Indexes: "parks_pkey" PRIMARY KEY, btree (id) "index_parks_on_name" btree (name) "index_parks_on_boundary" gist (polygon)

# select id, name, boundary from parks limit 2; id | name | boundary --------+-------------+------------------------ 1 | Yosemite |0103000020E6100000010000... 2 | Yellowstone |0103000020E6100000010000...

24Friday, April 19,

Page 25: PostGIS on Rails

How many bears are in Yosemite now?

##> park = Park.find_by_name(‘Yosemite’)

##> bears = Bear.where(‘ST_Intersects(coordinates, :bounds)’, :bounds => park.boundary)

##> bear_count = bears.count

25Friday, April 19,

Page 26: PostGIS on Rails

How Many Bears in Yosemite and Yellowstone

(Ruby)?

##> yosemite = Park.find_by_name(‘Yosemite’)

##> yellowstone = Park.find_by_name(‘Yellowstone’)

##> bounds = yosemite.boundary + yellowstone.boundary

##> bears = Bear.where(‘ST_Intersects(coordinates, :bounds)’, :bounds => bounds)

##> bear_count = bears.count

26Friday, April 19,

Page 27: PostGIS on Rails

How Many Bears in Yosemite and Yellowstone

(SQL)?

select count(*) from bears inner join parks on ST_Intersects(bears.coordinates, parks.boundary) where parks.name in (‘Yosemite’, ‘Yellowstone’);

27Friday, April 19,

Page 28: PostGIS on Rails

Three parks closest to me?

Distance operator (KNN) is a feature of Postgres 9.1 and above

select id, name, boundary <-> ST_Point(37.775, -122.44) as distancefrom parksorder by distancelimit 3;

28Friday, April 19,

Page 29: PostGIS on Rails

What else is possible?

29Friday, April 19,

Page 30: PostGIS on Rails

Geometry Simplification

ST_Simplify

ST_ConvexHull

ST_ConcaveHull

30Friday, April 19,

Page 31: PostGIS on Rails

Spatial Relationships

ST_Centroid

ST_Contained

ST_Area

ST_Perimeter

ST_DWithin

31Friday, April 19,

Page 32: PostGIS on Rails

Format Conversions

ST_AsGeoJSON

ST_AsText

32Friday, April 19,

Page 35: PostGIS on Rails

Data Sources (commercial)

Maponics

http://www.maponics.com/

Urban Mapping

http://www.urbanmapping.com/

Onboard Informatics

http://www.onboardinformatics.com/

35Friday, April 19,

Page 37: PostGIS on Rails

Q & A

We are hiring @apartmentlist!37Friday, April 19,