Skip to content

Commit

Permalink
Add Geometry#orient_polygons and #orient_polygons!.
Browse files Browse the repository at this point in the history
  • Loading branch information
dark-panda committed Aug 2, 2024
1 parent 2589261 commit 4d1ff2d
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 0 deletions.
5 changes: 5 additions & 0 deletions lib/ffi-geos.rb
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,11 @@ def self.geos_library_paths
:int, :pointer, :pointer
],

GEOSOrientPolygons_r: [
# -1 on exception, *handle, *geom, exterior_cw
:int, :pointer, :pointer, :int
],

GEOSGetInteriorRingN_r: [
# *geom, *handle, *geom, n
:pointer, :pointer, :pointer, :int
Expand Down
12 changes: 12 additions & 0 deletions lib/ffi-geos/geometry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ def normalize!
end
alias normalize normalize!

if FFIGeos.respond_to?(:GEOSOrientPolygons_r)
def orient_polygons!(exterior_cw = false)
raise Geos::GEOSException, self.class if FFIGeos.GEOSOrientPolygons_r(Geos.current_handle_pointer, ptr, bool_to_int(exterior_cw)) == -1

self
end

def orient_polygons(exterior_cw = false)
dup.orient_polygons!(exterior_cw)
end
end

def srid
FFIGeos.GEOSGetSRID_r(Geos.current_handle_pointer, ptr)
end
Expand Down
101 changes: 101 additions & 0 deletions test/geometry/orient_polygons_tests.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# frozen_string_literal: true

require 'test_helper'

describe '#orient_polygons' do
include TestHelper

def setup
super
writer.trim = true
end

it 'does not overwrite the original geometry' do
skip unless ENV['FORCE_TESTS'] || Geos::Geometry.method_defined?(:orient_polygons)

geom = read('POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))')

result = geom.orient_polygons(true)

assert_equal('POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))', write(geom))
assert_equal('POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))', write(result))
refute_same(geom, result)
end

it 'does overwrite the original geometry with bang method' do
skip unless ENV['FORCE_TESTS'] || Geos::Geometry.method_defined?(:orient_polygons!)

geom = read('POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))')

result = geom.orient_polygons!(true)

assert_equal('POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))', write(geom))
assert_equal('POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))', write(result))
assert_same(geom, result)
end

it 'handles empty polygons' do
skip unless ENV['FORCE_TESTS'] || Geos::Geometry.method_defined?(:orient_polygons)

simple_tester(
:orient_polygons,
'POLYGON EMPTY',
'POLYGON EMPTY'
)
end

it 'hole orientation is opposite to shell' do
skip unless ENV['FORCE_TESTS'] || Geos::Geometry.method_defined?(:orient_polygons)

simple_tester(
:orient_polygons,
'POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 1 2, 2 2, 2 1, 1 1))',
'POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))'
)

simple_tester(
:orient_polygons,
'POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))',
'POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))',
true
)
end

it 'ensures all polygons in collection are processed' do
skip unless ENV['FORCE_TESTS'] || Geos::Geometry.method_defined?(:orient_polygons)

simple_tester(
:orient_polygons,
'MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 1 2, 2 2, 2 1, 1 1)), ((100 100, 200 100, 200 200, 100 100)))',
'MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1)), ((100 100, 200 100, 200 200, 100 100)))'
)

simple_tester(
:orient_polygons,
'MULTIPOLYGON (((0 0, 0 10, 10 10, 10 0, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1)), ((100 100, 200 200, 200 100, 100 100)))',
'MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1)), ((100 100, 200 100, 200 200, 100 100)))',
true
)
end

it 'polygons in collection are oriented, closed linestring unchanged' do
skip unless ENV['FORCE_TESTS'] || Geos::Geometry.method_defined?(:orient_polygons)

simple_tester(
:orient_polygons,
'GEOMETRYCOLLECTION (POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1)), LINESTRING (100 100, 200 100, 200 200, 100 100))',
'GEOMETRYCOLLECTION (POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1)), LINESTRING (100 100, 200 100, 200 200, 100 100))',
true
)
end

it 'nested collection handled correctly' do
skip unless ENV['FORCE_TESTS'] || Geos::Geometry.method_defined?(:orient_polygons)

simple_tester(
:orient_polygons,
'GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0)))))',
'GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0)))))'
)
end
end

0 comments on commit 4d1ff2d

Please sign in to comment.