New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change SphericalMath::PointXYZ to implement BigDecimal instead of Float #213
base: main
Are you sure you want to change the base?
Conversation
…he mathematical calculations yet because it takes a very long time to compute
…YZ::intersects_arc?
Would love if this could get merged! |
If you need to use the fix immediately and you're using a Gemfile/Bundler, you can add this to the Gemfile to build from the branch.
Just make sure to change it back once it's merged in. If you use the branch and are still getting errors, post in the original issue and I can take a look. |
…ypes to BigDecimal
Instead of forcing Latitudes and longitudes on points will still be represented as floats. usage: |
@keithdoggett I've been bitten by this issue several times, and I really appreciate your efforts to fix the issue. I was wondering what the performance implications of this change are. I created a test to validate that this patch fixes my issue and noticed the test taking another 1.3s (0.3s vs 1.6s). I was also wondering whether there might be an alternative approach where we continue to use floats but only trigger |
@jdalt thanks for the reply and testing out the code. Definitely appreciate the feedback. You're right that the performance with decimals instead of floats is not nearly as good. I tried to alleviate that somewhat with my last update to make the option to use decimals configurable at the factory level, but it's still slower in cases you need to use it and the way that option is passed around isn't the cleanest. Your approach sounds interesting, but I need a little bit of clarification. When you say the float passes a certain configurable threshold, do you mean distance from another point or how precise a coordinate the float is representing? |
@keithdoggett My idea was to use a threshold for detecting arc intersection. I'm not certain this would be a viable solution though since it sacrifices correctness, and I'm realizing these rounding errors actually let several invalid polygons into my dataset. Ultimately I've worked around this by passing |
Gotcha. At this point, it does seem to be a trade-off between correctness and performance. One theory that I haven't gotten around to testing is that the inaccuracies arise from converting from a spherical system to cartesian in I think that the solution you posed is the best at this point, though. Do the validations in a projection or some other factory and then spherical calculations in a factory with lenient assertions. |
I'd be interested to see dummy examples of the following:
I would imagine that one of these happens when the geometry is very small (the dense packing problem) and one happens when things are very large. It would be nice to develop some guidance as to when these are important. For both of these edge case situations, the spherical factory with decimals would be necessary. For everything else you might as well use a CAPI backed projected coordinate system. Knowing the heuristic for which factory to select is key. |
hey there, did anybody find another workaround for this? |
@waissbluth we haven't made any progress on this issue specifically, although there have been discussions about potential solutions that we'll try to implement at some point in the future. But we have been reworking the validity handling for version 3 (#271) so that errors won't be raised on instantiation and validity will only be checked on an as-needed basis (ex. There is still the option to pass the |
Thank you @keithdoggett, this works:
Surprisingly (?), this also solves the issue:
Which is surprising since I thought that was the default behavior and therefore adding that could should not have an effect on this bug? For reference, this is the geometry that is causing trouble: |
The You could do something like this RGeo::ActiveRecord::SpatialFactoryStore.instance.tap do |store|
store.default = RGeo::Geos.factory_generator
store.register(RGeo::Geographic.spherical_factory(srid: 4326, uses_lenient_assertions: true), {sql_type: 'geography'})
end To send all geographic columns to the specified spherical factory and all geometry columns to the default factory. Let me know if that helps. |
Summary
Resolves #212
Valid polygons will sometimes fail the linear ring test in spherical factories if they have a high point density. The test erroneously detects that arcs of the polygon intersect causing it to fail the
is_simple?
condition. After some testing, I found out the issue has to do with floats lacking enough precision in the spherical coordinate system.To fix this, I cast the floats to
BigDecimal
s before any calculations are done on them. This has so far worked for all of the cases I've found of the issue.Other Information
I attempted to integrate the
BigMath
library into the class to handle trigonometric and sqrt calculations but it made the tests take about 240x longer. I didn't notice any increase in accuracy by using it so removed it.