rgeo / activerecord-postgis-adapter

ActiveRecord connection adapter for PostGIS, based on postgresql and rgeo
Other
878 stars 241 forks source link

RGeo::Error::UnsupportedOperation: Method Surface#area not defined with 7.1.1 #365

Closed viktorianer closed 2 years ago

viktorianer commented 2 years ago

We would like to upgrade to version 7.x, but experiencing issues, if we do so.

field = Field.where.not(poly: nil).first
poly = field.poly
poly.area
# RGeo::Error::UnsupportedOperation: Method Surface#area not defined.

On the latest 6.x version, we do not have this error. In our Gemfile we use, next to this gem:

gem "activerecord-postgis-adapter", "~> 7.0"
gem "rgeo", "~> 2.2"
gem "rgeo-geojson", "~> 2.1"
gem "rgeo-shapefile", "~> 3.0"
gem "rgeo-proj4", "~> 2.0"

I'm on:

Rails 6.1.6
Ruby 3.0.4
activerecord-postgis-adapter 7.1.1
keithdoggett commented 2 years ago

Typically when a method like area is undefined that means you're inadvertently using a non-GEOS backed factory. What is the result of poly.factory? Are you setting a default factory like the example in this section (https://github.com/rgeo/activerecord-postgis-adapter#configuring-activerecord)?

viktorianer commented 2 years ago

Hey Keith, thank you for fast reply.

Typically when a method like area is undefined that means you're inadvertently using a non-GEOS backed factory. What is the result of poly.factory? Are you setting a default factory like the example in this section (https://github.com/rgeo/activerecord-postgis-adapter#configuring-activerecord)?

I do not think our config makes any difference, because why it would work on 6.x but not on 7.x? Anyway, yes, we do have this config:

RGeo::ActiveRecord::SpatialFactoryStore.instance.tap do |config|
  config.default = RGeo::Geos.factory_generator
  config.register(RGeo::Geographic.spherical_factory(srid: 4326), geo_type: "polygon")
  config.register(RGeo::Geographic.spherical_factory(srid: 4326), geo_type: "point")
  config.register(RGeo::Geographic.simple_mercator_factory(srid: 4326), geo_type: "polyline")
end

And you are right, the returned factory is different (I did check it before posting it here, but did not mention it):

# 6.x
poly.factory
# <RGeo::Geos::CAPIFactory:0x28f78 srid=4326 bufres=1 flags=8>

# 7.x
poly.factory
# <RGeo::Geographic::Factory:0x0000561991661a10 @impl_prefix="Spherical", 
# @point_class=RGeo::Geographic::SphericalPointImpl,...

And yes, it is the reason for this issue.

One more thing. I didn't even notice it yesterday, because I've already finished work. Today I see that the DB can no longer be set up on our CI and locally, if downgrading to 6.x!

RAILS_ENV=test bin/rails db:setup
# 6.x
# undefined local variable or method `configuration' for 
# #<ActiveRecord::ConnectionAdapters::PostGIS::PostGISDatabaseTasks:0x00005581d5249648 
# @db_config=#<ActiveRecord::DatabaseConfigurations::UrlConfig:0x00005581d07e1b50 @env_name="test", 
# @name="primary", @configuration_hash={:adapter=>"postgis"...
# ...
# .bundle/ruby/3.0.0/gems/activerecord-postgis-adapter-6.0.1/
# lib/active_record/connection_adapters/postgis/postgis_database_tasks.rb:99:in `ensure_installation_configs'

# 7.x
Database '...' already exists

It was mentioned in https://github.com/rgeo/activerecord-postgis-adapter/issues/322 and fixed in 7.x with https://github.com/rgeo/activerecord-postgis-adapter/pull/324. But does not work for me? Maybe related to ActiveRecord::DatabaseConfigurations::UrlConfig?

So, at the end, I can not use neither the version 6.x nor the 7.x

viktorianer commented 2 years ago

I just saw, I can not upgrade to latest 6.x version, because:

gem "activerecord-postgis-adapter", "~> 6.0.3"
Bundler could not find compatible versions for gem "activerecord":
  In snapshot (Gemfile.lock):
    activerecord (= 6.1.6)

  In Gemfile:
    activerecord-postgis-adapter (~> 6.0.3) was resolved to 6.0.3, which depends on
      activerecord (~> 6.0.0)

    rails (~> 6.1.6) was resolved to 6.1.6, which depends on
      activerecord (= 6.1.6)

The 6.0.1 does not have this restriction to activerecord (~> 6.0.0)!

keithdoggett commented 2 years ago

@viktorianer version 7 relies on rgeo-activerecord version 7 which uses a different process for determining which factory to use. There's some examples here (https://github.com/rgeo/rgeo-activerecord/wiki/Spatial-Factory-Store#adding-factories).

Based on the configuration you sent me, in version 7, any geometry column with a geo_type of polygon will use a geographic factory. Perhaps you want to limit that to a certain SRID or geographic: true as well?

Let me know if that works. If not I can update the 60-stable branch to be restricted to activerecord ~>6.0.0.

viktorianer commented 2 years ago

Hi @keithdoggett. We are not sure, what is your suggestion here:

Based on the configuration you sent me, in version 7, any geometry column with a geo_type of polygon will use a geographic factory. Perhaps you want to limit that to a certain SRID or geographic: true as well?

How and where to set up RGeo, after upgrade from 6.x to 7.x?

Let me know if that works. If not I can update the 60-stable branch to be restricted to activerecord ~>6.0.0.

Probably ~> 6.0?

keithdoggett commented 2 years ago

@viktorianer how you have RGeo set up now is still correct for version 7. I would recommend using a GEOS backed factory in almost every case, especially when dealing with polygons since you have a lot more methods available to you.

changing:

  config.register(RGeo::Geographic.spherical_factory(srid: 4326), geo_type: "polygon")

to

  config.register(RGeo::Geos.factory(srid: 4326), geo_type: "polygon")

will use a GEOS factory for polygons again.

The only time I'd recommend using the spherical_factory is if you're dealing with geometries that are very very far apart to the point that the curvature of the earth has a noticeable effect on your computations, or when dealing with point to point operations, not polygons.

You might also consider using a projected coordinate system that stores data in meters. The 4326 SRID stores data in degrees so when you run poly.area your result is in degrees^2.

viktorianer commented 2 years ago

@keithdoggett Thanks a lot for your help.

I changed to config.register(RGeo::Geos.factory(srid: 4326), geo_type: "polygon") and now I can use 7.x as expected, I think so. Because now I get area in degrees^2:

# 7.x
poly.factory
# <RGeo::Geos::CAPIFactory:0x92130 srid=4326 bufres=1 flags=8>
poly.area
# 3.381987213114231e-06

Now I have to find out how to transform it in hectares, that what I need. We just used the following quarry for this, so far, and I would like to change this after upgrade:

# works for 6.x and 7.x
Field.find_by_sql("select st_area(st_transform(st_setsrid(st_geomfromtext('#{poly}'),4326),31467)) as area")
  .first.area.to_f / (100 * 100)

If you already know something better, I'd be very happy to hear about it.

You might also consider using a projected coordinate system that stores data in meters.

I think I will need to use proj4 for this?! I do not have it running on Heroku yet, and I would like to change on Rails upgrade as little as possible.

RGeo::CoordSys::Proj4.supported?
# => false

What about this ticket? Should we have some updated docs for upgrading from 6.x to 7.x? What about not working version 6.0.3?

keithdoggett commented 2 years ago

Hi @viktorianer glad to hear you have it working!

I think I will need to use proj4 for this?! I do not have it running on Heroku yet, and I would like to change on Rails upgrade as little as possible.

Yes, if you would like to reproject geometries in the Ruby app, you'll need to use the proj gem. It's possible to do all the transformations in PostGIS as you've shown above, but the queries can be tedious.

Should we have some updated docs for upgrading from 6.x to 7.x?

Yes I can add docs.

What about not working version 6.0.3?

What's the issue with 6.0.3 exactly? Per rubygems (https://rubygems.org/gems/activerecord-postgis-adapter/versions/6.0.3) it's pinned to activerecord ~>6.0.0 and the issue you described is that it wants to use activerecord 6.1+?

viktorianer commented 2 years ago

What about not working version 6.0.3?

What's the issue with 6.0.3 exactly? Per rubygems (https://rubygems.org/gems/activerecord-postgis-adapter/versions/6.0.3) it's pinned to activerecord ~>6.0.0 and the issue you described is that it wants to use activerecord 6.1+?

It should have activerecord ~>6.0 and not activerecord ~>6.0.0. Last one does not work, because it prevents an upgrade to activerecord 6.1+ with rails ~> 6.1.6

keithdoggett commented 2 years ago

Ahh yes thanks. Version 6.x of this adapter is not compatible with ActiveRecord 6.1. Due to some changes in the internals of ActiveRecord, we had to release version 7 to be compatible with ActiveRecord 6.1. The breaking changes are all in this PR if you're curious (https://github.com/rgeo/activerecord-postgis-adapter/pull/324).

keithdoggett commented 2 years ago

Added some documentation in #368