OpenStreetMap.org‘s RESTful API allows anyone to access data on their continually growing collaborative map of the world… in XML. This is great for most applications, but if you’re working in JavaScript (as we are), XML might as well be greek. We need JSON.
To offer OSM-JSON along with of OSM-XML, we added a route to accept a “.format” suffix, and split up the render call based on the params[:format] part of the route:
# /config/routes.rb:46-50 map.connect "api/#{API_VERSION}/geohash/:geohash.:format", :controller => 'api', :action => 'geohash' map.connect "api/#{API_VERSION}/geohash/:geohash", :controller => 'api', :action => 'geohash' map.connect "api/#{API_VERSION}/map.:format", :controller => 'api', :action => 'map' map.connect "api/#{API_VERSION}/map", :controller => 'api', :action => 'map'
Notice we also added a ‘geohash’ route. Whereas the /map call requires a bbox parameter (‘bbox=min_lon,min_lat,max_lon,max_lat’), we can use a geohash (Geohash in JavaScript, Geohash in Rails) which defines a bounding box as a sequence of letters and numbers. This fits Cartagen’s needs well, and since it doesn’t require any parameters, we can page cache it in Rails. (Remember that page caching bypasses Rails entirely, letting Apache handle these cached files at high speed – that saved us when we were on BoingBoing).
We then modify the api_controller to respond_to either .xml or .json:
# /app/controllers/api_controller.rb:284-287 respond_to do |format| format.xml { render :text => doc.to_s, :content_type => "text/xml" } format.json { render :json => {'osm' => doc} } end
However, to pack up our objects into JSON properly, we also had to modify the map method further, for nodes, ways, and relations:
# /app/controllers/api_controller.rb:186-190 if params[:format] == 'json' doc['node'] << node.to_json_obj else doc.root << node.to_xml_node(changeset_cache, user_display_name_cache) end
The above was repeated for ways and relations, lines 202 and 270.
Then, we added a geohash method to catch our new geohash route:
# /app/controllers/api_controller.rb:76-80 def geohash _bbox = GeoHash.decode_bbox(params[:geohash]) params['bbox'] = _bbox[0][1].to_s+','+_bbox[0][0].to_s+','+_bbox[1][1].to_s+','+_bbox[1][0].to_s map end
Finally, we cloned the to_json_obj methods in /models/node.rb, /models/way.rb, and /models/relation.rb, which was pretty minor – we just restructured how they're packed up.
As HTML5 and the new generation of JavaScript interpreters transform the web, I expect we'll see a lot more APIs implemented in JSON as well as XML, and I'd be happy to help anyone with an OSM Rails port get this set up. For the time being, feel free to use our OSM API (with JSON!) at http://cartagen.org/api/0.6/...
Download the modified code here: openstreetmap.zip
(based on OSM revision 15115)
Great stuff Jeffrey. I encourage you to post this to the OSM dev list, and ask for commit access to the SVN repo.