ffi-geos 0.0.1.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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