ffi-geos 0.0.1.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,28 @@
1
+
2
+ module Geos
3
+ class GeometryCollection < Geometry
4
+ include Enumerable
5
+
6
+ # Yields each Geometry in the GeometryCollection.
7
+ def each
8
+ self.num_geometries.times do |n|
9
+ yield self.get_geometry_n(n)
10
+ end
11
+ nil
12
+ end
13
+
14
+ def get_geometry_n(n)
15
+ if n < 0 || n >= self.num_geometries
16
+ nil
17
+ else
18
+ cast_geometry_ptr(FFIGeos.GEOSGetGeometryN_r(Geos.current_handle, self.ptr, n), false)
19
+ end
20
+ end
21
+
22
+ def [](*args)
23
+ self.to_a[*args]
24
+ end
25
+ alias :slice :[]
26
+ alias :at :[]
27
+ end
28
+ end
@@ -0,0 +1,61 @@
1
+
2
+ module Geos
3
+ class LineString < Geometry
4
+ include Enumerable
5
+
6
+ def each
7
+ self.num_points.times do |n|
8
+ yield self.point_n(n)
9
+ end
10
+ nil
11
+ end
12
+
13
+ if FFIGeos.respond_to?(:GEOSGeomGetNumPoints_r)
14
+ def num_points
15
+ FFIGeos.GEOSGeomGetNumPoints_r(Geos.current_handle, self.ptr)
16
+ end
17
+ else
18
+ def num_points
19
+ self.coord_seq.length
20
+ end
21
+ end
22
+
23
+ def point_n(n)
24
+ if n < 0 || n >= self.num_points
25
+ raise RuntimeError.new("Index out of bounds")
26
+ else
27
+ cast_geometry_ptr(FFIGeos.GEOSGeomGetPointN_r(Geos.current_handle, self.ptr, n))
28
+ end
29
+ end
30
+
31
+ def [](*args)
32
+ self.to_a[*args]
33
+ end
34
+ alias :slice :[]
35
+
36
+ def buffer_single_sided(width, options = {})
37
+ options = {
38
+ :quad_segs => 8,
39
+ :join => :round,
40
+ :mitre_limit => 5.0,
41
+ :left_side => false
42
+ }.merge(options)
43
+
44
+ cast_geometry_ptr(FFIGeos.GEOSSingleSidedBuffer_r(
45
+ Geos.current_handle,
46
+ self.ptr,
47
+ width,
48
+ options[:quad_segs],
49
+ options[:join],
50
+ options[:mitre_limit],
51
+ options[:left_side] ? 1 : 0
52
+ ))
53
+ end
54
+
55
+ if FFIGeos.respond_to?(:GEOSisClosed_r)
56
+ def closed?
57
+ bool_result(FFIGeos.GEOSisClosed_r(Geos.current_handle, self.ptr))
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,5 @@
1
+
2
+ module Geos
3
+ class LinearRing < LineString
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+
2
+ module Geos
3
+ class MultiLineString < GeometryCollection
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+
2
+ module Geos
3
+ class MultiPoint < GeometryCollection
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+
2
+ module Geos
3
+ class MultiPolygon < GeometryCollection
4
+ end
5
+ end
@@ -0,0 +1,22 @@
1
+
2
+ module Geos
3
+ class Point < Geometry
4
+ if FFIGeos.respond_to?(:GEOSGeomGetX_r)
5
+ def get_x
6
+ FFI::MemoryPointer.new(:double).tap { |ret|
7
+ FFIGeos.GEOSGeomGetX_r(Geos.current_handle, self.ptr, ret)
8
+ }.read_double
9
+ end
10
+ alias :x :get_x
11
+ end
12
+
13
+ if FFIGeos.respond_to?(:GEOSGeomGetY_r)
14
+ def get_y
15
+ FFI::MemoryPointer.new(:double).tap { |ret|
16
+ FFIGeos.GEOSGeomGetY_r(Geos.current_handle, self.ptr, ret)
17
+ }.read_double
18
+ end
19
+ alias :y :get_y
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+
2
+ module Geos
3
+ class Polygon < Geometry
4
+ def num_interior_rings
5
+ FFIGeos.GEOSGetNumInteriorRings_r(Geos.current_handle, self.ptr)
6
+ end
7
+
8
+ def interior_ring_n(n)
9
+ if n < 0 || n >= self.num_interior_rings
10
+ raise RuntimeError.new("Index out of bounds")
11
+ else
12
+ cast_geometry_ptr(FFIGeos.GEOSGetInteriorRingN_r(Geos.current_handle, self.ptr, n), false)
13
+ end
14
+ end
15
+
16
+ def exterior_ring
17
+ cast_geometry_ptr(FFIGeos.GEOSGetExteriorRing_r(Geos.current_handle, self.ptr), false)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,42 @@
1
+
2
+ module Geos
3
+ class PreparedGeometry
4
+ include Geos::Tools
5
+
6
+ attr_reader :ptr
7
+
8
+ def initialize(ptr, auto_free = true)
9
+ @ptr = FFI::AutoPointer.new(
10
+ ptr,
11
+ auto_free ? self.class.method(:release) : self.class.method(:no_release)
12
+ )
13
+ end
14
+
15
+ def self.no_release(ptr) #:nodoc:
16
+ end
17
+
18
+ def self.release(ptr) #:nodoc:
19
+ FFIGeos.GEOSPreparedGeom_destroy_r(Geos.current_handle, ptr)
20
+ end
21
+
22
+ def contains?(geom)
23
+ check_geometry(geom)
24
+ bool_result(FFIGeos.GEOSPreparedContains_r(Geos.current_handle, self.ptr, geom.ptr))
25
+ end
26
+
27
+ def contains_properly?(geom)
28
+ check_geometry(geom)
29
+ bool_result(FFIGeos.GEOSPreparedContainsProperly_r(Geos.current_handle, self.ptr, geom.ptr))
30
+ end
31
+
32
+ def covers?(geom)
33
+ check_geometry(geom)
34
+ bool_result(FFIGeos.GEOSPreparedCovers_r(Geos.current_handle, self.ptr, geom.ptr))
35
+ end
36
+
37
+ def intersects?(geom)
38
+ check_geometry(geom)
39
+ bool_result(FFIGeos.GEOSPreparedIntersects_r(Geos.current_handle, self.ptr, geom.ptr))
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,111 @@
1
+
2
+ module Geos
3
+ class STRtree
4
+ include Geos::Tools
5
+ include Enumerable
6
+
7
+ attr_reader :ptr
8
+
9
+ def initialize(capacity)
10
+ ptr = FFIGeos.GEOSSTRtree_create_r(Geos.current_handle, capacity)
11
+
12
+ @ptr = FFI::AutoPointer.new(
13
+ ptr,
14
+ self.class.method(:release)
15
+ )
16
+
17
+ @storage = {}
18
+ @storage_pointers = {}
19
+ @storage_key = 0
20
+ @built = false
21
+ end
22
+
23
+ def self.release(ptr) #:nodoc:
24
+ FFIGeos.GEOSSTRtree_destroy_r(Geos.current_handle, ptr)
25
+ end
26
+
27
+ def built?
28
+ @built
29
+ end
30
+
31
+ def next_key
32
+ @storage_key += 1
33
+ end
34
+ private :next_key
35
+
36
+ def insert(geom, item)
37
+ check_geometry(geom)
38
+
39
+ if self.built?
40
+ raise RuntimeError.new("STRtree has already been built")
41
+ else
42
+ key = next_key
43
+ key_ptr = FFI::MemoryPointer.new(:pointer)
44
+ key_ptr.write_int(key)
45
+
46
+ @storage[key] = item
47
+ @storage_pointers[key] = key_ptr
48
+ FFIGeos.GEOSSTRtree_insert_r(Geos.current_handle, self.ptr, geom.ptr, key_ptr)
49
+ end
50
+ end
51
+
52
+ def remove(geom, item)
53
+ check_geometry(geom)
54
+
55
+ key = if @storage.respond_to?(:key)
56
+ @storage.key(item)
57
+ else
58
+ @storage.index(item)
59
+ end
60
+
61
+ if key
62
+ key_ptr = @storage_pointers[key]
63
+ result = FFIGeos.GEOSSTRtree_remove_r(Geos.current_handle, self.ptr, geom.ptr, key_ptr)
64
+ @built = true
65
+
66
+ if result == 1
67
+ @storage[key] = nil
68
+ @storage_pointers[key] = nil
69
+ end
70
+ end
71
+ end
72
+
73
+ def query(geom)
74
+ check_geometry(geom)
75
+
76
+ @built = true
77
+ retval = nil
78
+
79
+ callback = if block_given?
80
+ proc { |*args|
81
+ key = args.first.read_int
82
+ yield(@storage[key])
83
+ }
84
+ else
85
+ retval = []
86
+ proc { |*args|
87
+ retval << @storage[args.first.read_int]
88
+ }
89
+ end
90
+
91
+ FFIGeos.GEOSSTRtree_query_r(
92
+ Geos.current_handle,
93
+ self.ptr,
94
+ geom.ptr,
95
+ callback,
96
+ nil
97
+ )
98
+
99
+ if retval
100
+ retval.compact
101
+ end
102
+ retval
103
+ end
104
+
105
+ def iterate
106
+ @storage.values.each do |v|
107
+ yield(v)
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,54 @@
1
+
2
+ module Geos
3
+ module Tools
4
+ include GeomTypes
5
+
6
+ def cast_geometry_ptr(geom_ptr, auto_free = true)
7
+ if geom_ptr.null?
8
+ raise RuntimeError.new("Tried to create a Geometry from a NULL pointer!")
9
+ end
10
+
11
+ klass = case FFIGeos.GEOSGeomTypeId_r(Geos.current_handle, geom_ptr)
12
+ when GEOS_POINT
13
+ Point
14
+ when GEOS_LINESTRING
15
+ LineString
16
+ when GEOS_LINEARRING
17
+ LinearRing
18
+ when GEOS_POLYGON
19
+ Polygon
20
+ when GEOS_MULTIPOINT
21
+ MultiPoint
22
+ when GEOS_MULTILINESTRING
23
+ MultiLineString
24
+ when GEOS_MULTIPOLYGON
25
+ MultiPolygon
26
+ when GEOS_GEOMETRYCOLLECTION
27
+ GeometryCollection
28
+ else
29
+ raise RuntimeError.new("Invalid geometry type")
30
+ end
31
+
32
+ klass.new(geom_ptr, auto_free)
33
+ end
34
+
35
+ def check_geometry(geom)
36
+ raise TypeError.new("Expected Geos::Geometry") unless geom.is_a?(Geos::Geometry)
37
+ end
38
+
39
+ def bool_result(r)
40
+ case r
41
+ when 1
42
+ true
43
+ when 0
44
+ false
45
+ else
46
+ raise RuntimeError.new("Unexpected boolean result: #{r}")
47
+ end
48
+ end
49
+
50
+ def check_enum_value(enum, value)
51
+ raise TypeError.new("Couldn't find valid #{enum.tag} value: #{value}") unless enum[value]
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,145 @@
1
+
2
+ module Geos
3
+ module Utils
4
+ class << self
5
+ include Geos::Tools
6
+
7
+ if FFIGeos.respond_to?(:GEOSOrientationIndex_r)
8
+ # Available in GEOS 3.3+.
9
+ def orientation_index(ax, ay, bx, by, px, py)
10
+ FFIGeos.GEOSOrientationIndex_r(
11
+ Geos.current_handle,
12
+ ax, ay, bx, by, px, py
13
+ )
14
+ end
15
+ end
16
+
17
+ if FFIGeos.respond_to?(:GEOSRelatePatternMatch_r)
18
+ # Available in GEOS 3.3+.
19
+ def relate_match(mat, pat)
20
+ bool_result(FFIGeos.GEOSRelatePatternMatch_r(Geos.current_handle, mat, pat))
21
+ end
22
+ end
23
+
24
+ def create_point(cs)
25
+ if cs.length != 1
26
+ raise RuntimeError.new("IllegalArgumentException: Point coordinate list must contain a single element")
27
+ end
28
+
29
+ cast_geometry_ptr(FFIGeos.GEOSGeom_createPoint_r(Geos.current_handle, cs.ptr)).tap {
30
+ cs.ptr.autorelease = false
31
+ }
32
+ end
33
+
34
+ def create_line_string(cs)
35
+ if cs.length <= 1 && cs.length != 0
36
+ raise RuntimeError.new("IllegalArgumentException: point array must contain 0 or >1 elements")
37
+ end
38
+
39
+ cast_geometry_ptr(FFIGeos.GEOSGeom_createLineString_r(Geos.current_handle, cs.ptr)).tap {
40
+ cs.ptr.autorelease = false
41
+ }
42
+ end
43
+
44
+ def create_linear_ring(cs)
45
+ if cs.length <= 1 && cs.length != 0
46
+ raise RuntimeError.new("IllegalArgumentException: point array must contain 0 or >1 elements")
47
+ end
48
+
49
+ cast_geometry_ptr(FFIGeos.GEOSGeom_createLinearRing_r(Geos.current_handle, cs.ptr)).tap {
50
+ cs.ptr.autorelease = false
51
+ }
52
+ end
53
+
54
+ def create_polygon(outer, *inner)
55
+ inner = Array(inner).flatten.tap { |i|
56
+ if i.detect { |g| !g.is_a?(Geos::LinearRing) }
57
+ raise TypeError.new("Expected inner Array to contain Geometry::LinearRing objects")
58
+ end
59
+ }
60
+
61
+ ary = FFI::MemoryPointer.new(:pointer, inner.length)
62
+ ary.write_array_of_pointer(inner.map(&:ptr))
63
+
64
+ cast_geometry_ptr(FFIGeos.GEOSGeom_createPolygon_r(Geos.current_handle, outer.ptr, ary, inner.length)).tap {
65
+ outer.ptr.autorelease = false
66
+ inner.each { |i| i.ptr.autorelease = false }
67
+ }
68
+ end
69
+
70
+ def create_empty_point
71
+ cast_geometry_ptr(FFIGeos.GEOSGeom_createEmptyPoint_r(Geos.current_handle))
72
+ end
73
+
74
+ def create_empty_line_string
75
+ cast_geometry_ptr(FFIGeos.GEOSGeom_createEmptyLineString_r(Geos.current_handle))
76
+ end
77
+
78
+ def create_empty_polygon
79
+ cast_geometry_ptr(FFIGeos.GEOSGeom_createEmptyPolygon_r(Geos.current_handle))
80
+ end
81
+
82
+ def create_empty_collection(t)
83
+ cast_geometry_ptr(FFIGeos.GEOSGeom_createEmptyCollection_r(Geos.current_handle, t))
84
+ end
85
+
86
+ def create_empty_multi_point
87
+ create_empty_collection(Geos::GeomTypes::GEOS_MULTIPOINT)
88
+ end
89
+
90
+ def create_empty_multi_line_string
91
+ create_empty_collection(Geos::GeomTypes::GEOS_MULTILINESTRING)
92
+ end
93
+
94
+ def create_empty_multi_polygon
95
+ create_empty_collection(Geos::GeomTypes::GEOS_MULTIPOLYGON)
96
+ end
97
+
98
+ def create_empty_geometry_collection
99
+ create_empty_collection(Geos::GeomTypes::GEOS_GEOMETRYCOLLECTION)
100
+ end
101
+
102
+ def create_collection(t, *geoms)
103
+ klass = case t
104
+ when Geos::GeomTypes::GEOS_MULTIPOINT
105
+ Geos::Point
106
+ when Geos::GeomTypes::GEOS_MULTILINESTRING
107
+ Geos::LineString
108
+ when Geos::GeomTypes::GEOS_MULTIPOLYGON
109
+ Geos::Polygon
110
+ when Geos::GeomTypes::GEOS_GEOMETRYCOLLECTION
111
+ Geos::Geometry
112
+ end
113
+
114
+ geoms = Array(geoms).flatten.tap { |i|
115
+ if i.detect { |g| !g.is_a?(klass) }
116
+ raise TypeError.new("Expected geoms Array to contain #{klass} objects")
117
+ end
118
+ }
119
+
120
+ ary = FFI::MemoryPointer.new(:pointer, geoms.length)
121
+ ary.write_array_of_pointer(geoms.map(&:ptr))
122
+
123
+ cast_geometry_ptr(FFIGeos.GEOSGeom_createCollection_r(Geos.current_handle, t, ary, geoms.length)).tap {
124
+ geoms.each { |i| i.ptr.autorelease = false }
125
+ }
126
+ end
127
+
128
+ def create_multi_point(*geoms)
129
+ create_collection(Geos::GeomTypes::GEOS_MULTIPOINT, *geoms)
130
+ end
131
+
132
+ def create_multi_line_string(*geoms)
133
+ create_collection(Geos::GeomTypes::GEOS_MULTILINESTRING, *geoms)
134
+ end
135
+
136
+ def create_multi_polygon(*geoms)
137
+ create_collection(Geos::GeomTypes::GEOS_MULTIPOLYGON, *geoms)
138
+ end
139
+
140
+ def create_geometry_collection(*geoms)
141
+ create_collection(Geos::GeomTypes::GEOS_GEOMETRYCOLLECTION, *geoms)
142
+ end
143
+ end
144
+ end
145
+ end