neo4j Spatial: Indexing football stadiums using the REST API
Date: 2013-06-24 07:17:15

Late last week my colleague https://twitter.com/peterneubauer[Peter] wrote up some documentation about http://neo4j.github.io/spatial/#spatial-server-plugin[creating spatial indexes in neo4j via HTTP], something I hadn't realised was possible until then.

I previously wrote about http://www.markhneedham.com/blog/2013/03/10/neo4jcypher-finding-football-stadiums-near-a-city-using-spatial/[indexing football stadiums using neo4j spatial] but the annoying thing about the approach I described was that I was using neo4j in http://docs.neo4j.org/chunked/stable/tutorials-java-embedded.html[embedded mode] which restricts you to using a JVM language.

The rest of my code is in Ruby so I thought I'd translate that code.

To recap, I'm parsing a CSV file of football stadiums that I downloaded from http://www.doogal.co.uk/FootballStadiums.php[Chris Bell's blog] which looks like this:

[source,text]
----

Name,Team,Capacity,Latitude,Longitude
"Adams Park","Wycombe Wanderers",10284,51.6306,-0.800299
"Almondvale Stadium","Livingston",10122,55.8864,-3.52207
"Amex Stadium","Brighton and Hove Albion",22374,50.8609,-0.08014
----

The code to process the file and index the stadiums in neo4j is https://github.com/mneedham/neo4j-football-stadiums/blob/master/create_stadiums.rb[as follows] (and is essentially a translation of the +++<cite>+++https://github.com/neo4j/spatial/blob/413317048eee7c2d3b25950d361634fc20c238d2/src/test/java/org/neo4j/gis/spatial/SpatialPluginFunctionalTest.java#L238[find_geometries_within_distance_using_cypher]+++</cite>+++ test):

[source,ruby]
----

require 'csv'
require 'httparty'
require 'json'

HTTParty.post("http://localhost:7474/db/data/ext/SpatialPlugin/graphdb/addSimplePointLayer",
  :body => { :layer => 'geom', :lat => 'lat', :lon => 'lon' }.to_json,
  :headers => { 'Content-Type' => 'application/json' } )

HTTParty.post("http://localhost:7474/db/data/index/node", 		
  :body => { :name => 'geom', :config => { :provider => 'spatial', :geometry_type => 'point', :lat => 'lat', :lon => 'lon'  } }.to_json,
  :headers => { 'Content-Type' => 'application/json' } )

contents = CSV.read(File.join(File.dirname(__FILE__), 'data', 'stadiums.csv'))
contents.shift
contents.each do |row|
  name, team, capacity, lat, long = row

  node_id = HTTParty.post("http://localhost:7474/db/data/node", 		
    :body => { :lat => lat.to_f, :lon => long.to_f, :name => name, :team => team, :capacity => capacity }.to_json,
    :headers => { 'Content-Type' => 'application/json' } )['self'].split("/")[-1]

  HTTParty.post("http://localhost:7474/db/data/index/node/geom", 		
    :body => { :key => 'dummy', :value => 'dummy', :uri => "http://localhost:7474/db/data/node/#{node_id}"}.to_json,
    :headers => { 'Content-Type' => 'application/json' } )
end
----

One change from the previous version is that I'm not indexing the stadiums using point based geometry rather than wkt.

If we want to find the number of stadiums within 10 km of Centre Point in London we'd write the following query:

[source,cypher]
----

START node = node:geom('withinDistance:[51.521348,-0.128113, 10.0]')
RETURN node.name, node.team;
----

[source,text]
----

==> +--------------------------------------------+
==> | node.name          | node.team             |
==> +--------------------------------------------+
==> | "Emirates Stadium" | "Arsenal"             |
==> | "Stamford Bridge"  | "Chelsea"             |
==> | "The Den"          | "Millwall"            |
==> | "Loftus Road"      | "Queens Park Rangers" |
==> | "Craven Cottage"   | "Fulham"              |
==> | "Brisbane Road"    | "Leyton Orient"       |
==> +--------------------------------------------+
==> 6 rows
----

I have https://github.com/mneedham/neo4j-football-stadiums[put the code on github] in case you're interested in playing around with it.