Polygons and MultiPolygons should follow the right-hand rule
januszm opened this issue ยท 8 comments
Since GeoJSON is now a formal specification https://tools.ietf.org/html/rfc7946#section-3.1.6 I believe that RGeo::GeoJSON.encode
should return a valid GeoJSON result.
One thing is to detect if polygons are right-hand wound (I guess Ruby Vector[].cross_product()
method from the matrix
module could do, some inspirations: https://github.com/mapbox/geojsonhint/blob/master/lib/rhr.js).
Second thing is to properly rewind the polygons (this JS package does the job: https://github.com/mapbox/geojson-rewind).
Let me know if this issue was already discussed or if someone already has a solution for this problem.
# https://github.com/alexreisner/geocoder/blob/master/lib/geocoder/calculations.rb
def to_radians(*args)
args = args.first if args.first.is_a?(Array)
if args.size == 1
args.first * (Math::PI / 180)
else
args.map{ |i| to_radians(i) }
end
end
# https://github.com/mapbox/geojsonhint/blob/master/lib/rhr.js
def is_ring_clockwise(coordinates)
area = 0
return if coordinates.size < 3
coordinates.each_slice(2) do |point_1, point_2|
area += to_radians(point_2[0] - point_1[0]) * (2 + Math.sin(to_radians(point_1[1])) + Math.sin(to_radians(point_2[1])))
end
area >= 0
end
# For MULTIPOLYGON
> shapefile = RGeo::Shapefile::Reader.open(my_shape_file)
> record = shapefile.next
> coordinates = record.geometry.coordinates.first.first # MultiPolygon->Polygon => [[x1, y1], ...]
> is_ring_clockwise(coordinates)
=> false
> coordinates_reversed = record.geometry.coordinates.map { |multipoly| multipoly.map { |poly| poly.reverse } }.first.first
> is_ring_clockwise(coordinates_reversed)
=> true
Something along those lines, it has to work for both exterior and interior rings, for each Polygon
inside a Multipolygon
, just like the orient
method in Python's shapely
package: https://github.com/Toblerity/Shapely/blob/master/shapely/geometry/polygon.py#L388
It should be even easier with PostGIS 2.4+. With the postgis
adapter, we should be able to offload all computations to PostGIS functions http://postgis.net/docs/ST_ForcePolygonCW.html
Thanks for opening this issue and sharing some possible solutions.
Here's what the spec says:
* A linear ring MUST follow the right-hand rule with respect to the
area it bounds, i.e., exterior rings are counterclockwise, and
holes are clockwise.
Note: the [GJ2008] specification did not discuss linear ring winding
order. For backwards compatibility, parsers SHOULD NOT reject
Polygons that do not follow the right-hand rule.
This gem can't use the PostGIS function (although that is helpful and a good way to check).
RGeo::Cartesian::Analysis.ring_direction
returns the orientation of a linestring (clockwise or counter-clockwise): https://github.com/rgeo/rgeo/blob/master/lib/rgeo/cartesian/analysis.rb#L22
Maybe we could add an option (clean
) to the initializer of RGeo::GeoJSON::Coder
. If clean
is true, then polygons are forced to be right-hand wound. The default for clean
should be false, since it might be slow and the spec says that parsers SHOULD NOT reject Polygons that do not follow the right-hand rule
.
OR we could add a polygon cleaning method to rgeo
, and leave this gem unchanged.
Thanks for the hint about RGeo::Cartesian::Analysis.ring_direction
, we could use that. And I agree about PostGIS
functions, I just wanted to share that finding in my last comment.
I was thinking about adding an option, something like strict: true/false
, or clean
like you proposed (naming things ... :) ) .
I think that this is more GeoJSON gem's responsibility - formatting the GeoJSON output properly. Users might want to keep the polygons source untouched (eg. db).
On the other hand, I can imagine that, for performance reasons, most users will want to clean their polygons in the database anyway.
Is there any follow-up to this issue? I searched RGeo repo and didn't find anything, maybe I missed it. I'm currently struggling whether this should be solved in RGeo or PostGIS side.
@januszm I've added this feature in #47. Since we're a bit short on maintainers, I'd be glad that you be part of the reviewers, at least for the dedicated commit (the last one)!
@ilvez the follow-up is coming. I think this should only be part of rgeo-geojson, I don't see why we should add such a restriction to rgeo.