lessless
lessless

Reputation: 896

Specify point type for RGeo-ActiveRecord column

there is a typical schema when Address model has an RGeo attribute:

t.st_point :coordinates,   geographic: true, srid: 4326

and normally it is wrapped in the RGeo::Geographic::SphericalPointImpl class

Realty.last.address.coordinates
#<RGeo::Geographic::SphericalPointImpl:0x2b1a364b429c "POINT (106.5 10.5)">

but in some situations, it is wrapped with completely inappropriate Cartesian wrapper RGeo::Cartesian::PointImpl

Realty.joins(:address).select('realties.id, addresses.coordinates::geometry').first.coordinates
#<RGeo::Cartesian::PointImpl:0x2b1a364a691c "POINT (106.0 10.0)">

I'm using latest 'activerecord-postgis-adapter 3.1.4' with rails 4.2.4

Maybe anybody know how a way to fix this, i.e. make coordinates always return instance of RGeo::Geographic::SphericalPointImpl?

Upvotes: 2

Views: 1679

Answers (1)

tee
tee

Reputation: 4409

When you select the column with addresses.coordinates::geometry, you are forcing Postgres to return a column of type geometry. When you do Realty.last.address.coordinates, you are returning a different SQL type (a point).

I would remove the ::geometry from your SQL query.

From the docs at https://github.com/rgeo/rgeo-activerecord#spatial-factories-for-columns:

activerecord-postgis-adapter converts the SQL type to a ruby type using the SpatialFactoryStore class as the registry to lookup types.

Register spatial factories in the SpatialFactoryStore singleton class. Each spatial type in your ActiveRecord models will use the SpatialFactoryStore to retrieve a factory matching the properties of its type. For example, you can set a different spatial factory for point types, or for types matching a specific SRID, or having a Z coordinate, or any combination of attributes.

The supported keys when registering a spatial type are listed here with their default values and other allowed values:

geo_type: "geometry", # point, polygon, line_string, geometry_collection, 
                      # multi_line_string, multi_point, multi_polygon
has_m:    false,      # true
has_z:    false,      # true
sql_type: "geometry", # geography
srid:     0,          # (any valid SRID)

The default factories are RGeo::Geographic.spherical_factory for geographic types, and RGeo::Cartesian.preferred_factory for geometric types.

Here is an example setup:

RGeo::ActiveRecord::SpatialFactoryStore.instance.tap do |config|
  # By default, use the GEOS implementation for spatial columns.
  config.default = RGeo::Geos.factory_generator

  # But use a geographic implementation for point columns.
  config.register(RGeo::Geographic.spherical_factory(srid: 4326), geo_type: "point")
end

Upvotes: 2

Related Questions