views:

171

answers:

3

I'm using PostGIS (1.5.1) with Rails (3.0.0rc) and a Google Map (v3), and I'm looking for a way to query the database to retrieve all the points within a polygon (specifically, the bounds of the map viewport). At present I am sending the coordinates of the bounds to the server as such:

google.maps.event.addListener(map, 'bounds_changed', function() {
  var bounds = map.getBounds();
  var ne = bounds.getNorthEast();
  var sw = bounds.getSouthWest();
  var yMaxLat = ne.lat();
  var xMaxLng = ne.lng();
  var yMinLat = sw.lat();
  var xMinLng = sw.lng();
  //alert("bounds changed");
  updateMap(yMaxLat, xMaxLng, yMinLat, xMinLng);
});

function updateMap(yMaxLat, xMaxLng, yMinLat, xMinLng) {
  jQuery.getJSON("/places", { "yMaxLat": yMaxLat, "xMaxLng": xMaxLng, "yMinLat": yMinLat, "xMinLng": xMinLng }, function(json) {
    if (json.length > 0) {
      for (i=0; i<json.length; i++) {
        var place = json[i];
        var category = json[i].tag;
        addLocation(place,category);
      }
    }
  });
}

Then in my controller:

format.js do
  xMinLng = params[:xMinLng]
  yMinLat = params[:yMinLat]
  xMaxLng = params[:xMaxLng]
  yMaxLat = params[:yMaxLat]

  @map_places = Place.find(:all, :conditions => ["geom && ?", Polygon.from_coordinates([[[xMinLng, yMinLat], [xMinLng, yMaxLat], [xMaxLng, yMaxLat], [xMaxLng, yMinLat], [xMinLng, yMinLat]]], 4326)])
  render :json => @map_places
end

I'm basing my query on this example, but it isn't really behaving as expected - most points are shown when the map is zoomed right out, but not those nearish to the international date line (ie. in Australia and New Zealand). The points aren't shown at all when the map is zoomed in, for any region of the globe. I've read something about PostGIS's geography type being more appropriate for this kind of query, but I don't think that such a type is supported by GeoRuby (it is supported by GeoDjango). The output from my console for such a request is below:

Started GET "/places?yMaxLat=63.93767251746141&xMaxLng=43.033828125&yMinLat=36.88016688406946&xMinLng=-39.407578125" for 127.0.0.1 at Tue Aug 24 12:11:41 +0100 2010
Processing by PlacesController#index as JSON
Parameters: {"xMinLng"=>"-39.407578125", "yMaxLat"=>"63.93767251746141", "xMaxLng"=>"43.033828125", "yMinLat"=>"36.88016688406946"}
Place Load (0.4ms)  SELECT "places".* FROM "places"
Place Load (0.8ms)  SELECT "places".* FROM "places" WHERE (geom && '0103000020E6100000010000000500000052B81E852BB443C0DF0CF74EA970424052B81E852BB443C0686D2EA705F84F40AE47E17A54844540686D2EA705F84F40AE47E17A54844540DF0CF74EA970424052B81E852BB443C0DF0CF74EA9704240')
Completed 200 OK in 136ms (Views: 3.4ms | ActiveRecord: 1.2ms)

What is the best way to go about constructing such a query? I'm using the GeoRuby and spatial_adapter gems with my project, but I'm struggling to find much information on using them as I've described above. Would a distance query be the better approach to take perhaps? Thanks!

EDIT: my migration is as below:

class CreatePlaces < ActiveRecord::Migration
  def self.up
    create_table :places do |t|
      t.string :city_name
      t.integer :country_id, :null => false
      t.point :geom, :null => false, :with_z => true, :srid => 4326
      t.string :name, :null => false
      t.string :tag, :null => false

      t.timestamps
    end
add_index :places, :geom, :spatial => true
  end

  def self.down
    drop_table :places
  end
  remove_index :places, :geom
end
A: 

There may be two different types of projection going on. Make sure the projection is the same in the database and in your application.

Preston Marshall
Thanks for your reply, I've included my database migration above to provide a few more details...
Sonia
Hmm, it looks like a valid SRID. Have you checked to see if the points actually show up from the SQL select? I would try to narrow down the issue to either the database or google maps. I have found some instances where GMaps won't put the points at a place on the map.
Preston Marshall
Points in the UK and Africa show up fine, but only at a certain zoom level - about 3 and below. Point in NZ and Australia never show up, but I'm assuming that's possibly a date line problem...Google Maps seems to be sending the bounds fine, I think it's a problem with my query - if I render all the points as JSON (so without the query) they show up on the map perfectly!
Sonia
Hmm, there may be an issue with the query, but it seems to work for the other guy. Maybe you could try doing the query with plain sql instead of GeoRuby, at least to see what it returns? Here are the docs on it: http://postgis.refractions.net/documentation/manual-1.5/ch04.html#id2794107
Preston Marshall
+1  A: 

Get out a standard map of the world (zero longitude in the middle, dateline at the edges). Plot the corner points of your box on the map. Join those points, using only lines that stay inside the map. That is the box that is querying the database. That's why your Aus/NZ points aren't coming out of the query. You have to either use the geography type or notice when your box is date-line crossing and split it into two non-crossing query boxes.

Paul Ramsey
A: 

Thanks for all the input - I'm afraid this actually turned out to be a massive n00b error on my part, in getting the correlation between x/y and lng/lat mixed up, hence why the points on the map showed fine when zoomed out, but disappeared when zoomed in. I'd thoroughly recommend QGIS (thanks to scw on http://gis.stackexchange.com/ for suggesting it) for anyone starting out with GIS, I immediately saw the error when the points were shown upside down!! Thanks again all!

BTW, slightly surprisingly, the points in NZ/Australia show up fine even when the bounding box crosses the international date line, although that might change if I add some more points in that area.

Sonia