GeoRuby 0.0.1
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.
- data/MIT-LICENSE +7 -0
- data/README +22 -0
- data/lib/geo_ruby/simple_features/ewkb_parser.rb +185 -0
- data/lib/geo_ruby/simple_features/geometry.rb +87 -0
- data/lib/geo_ruby/simple_features/geometry_collection.rb +94 -0
- data/lib/geo_ruby/simple_features/geometry_factory.rb +51 -0
- data/lib/geo_ruby/simple_features/line_string.rb +109 -0
- data/lib/geo_ruby/simple_features/linear_ring.rb +27 -0
- data/lib/geo_ruby/simple_features/multi_line_string.rb +35 -0
- data/lib/geo_ruby/simple_features/multi_point.rb +40 -0
- data/lib/geo_ruby/simple_features/multi_polygon.rb +38 -0
- data/lib/geo_ruby/simple_features/point.rb +85 -0
- data/lib/geo_ruby/simple_features/polygon.rb +103 -0
- data/lib/geo_ruby.rb +12 -0
- data/rakefile.rb +50 -0
- data/test/test_ewkb_parser.rb +91 -0
- data/test/test_simple_features.rb +303 -0
- metadata +62 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'geo_ruby/simple_features/geometry_collection'
|
2
|
+
|
3
|
+
|
4
|
+
module GeoRuby
|
5
|
+
module SimpleFeatures
|
6
|
+
#Represents a group of polygons (see Polygon).
|
7
|
+
class MultiPolygon < GeometryCollection
|
8
|
+
def initialize(srid = DEFAULT_SRID)
|
9
|
+
super(srid)
|
10
|
+
end
|
11
|
+
def binary_geometry_type
|
12
|
+
6
|
13
|
+
end
|
14
|
+
#Text representation of a MultiPolygon
|
15
|
+
def text_representation(dimension=2)
|
16
|
+
@geometries.collect{|polygon| "(" + polygon.text_representation(dimension) + ")"}.join(",")
|
17
|
+
end
|
18
|
+
#WKT geometry type
|
19
|
+
def text_geometry_type
|
20
|
+
"MULTIPOLYGON"
|
21
|
+
end
|
22
|
+
|
23
|
+
#Creates a multi polygon from an array of polygons
|
24
|
+
def self.from_polygons(polygons,srid=DEFAULT_SRID)
|
25
|
+
multi_polygon = MultiPolygon::new(srid)
|
26
|
+
multi_polygon.concat(polygons)
|
27
|
+
multi_polygon
|
28
|
+
end
|
29
|
+
#Creates a multi polygon from sequences of points : ((((x,y)...(x,y)),((x,y)...(x,y)),((x,y)...(x,y)))
|
30
|
+
def self.from_raw_point_sequences(point_sequences, srid= DEFAULT_SRID)
|
31
|
+
multi_polygon = MultiPolygon::new(srid)
|
32
|
+
multi_polygon.concat( point_sequences.collect {|point_sequence| Polygon.from_raw_point_sequences(point_sequence,srid) } )
|
33
|
+
multi_polygon
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require "geo_ruby/simple_features/geometry"
|
2
|
+
|
3
|
+
module GeoRuby
|
4
|
+
module SimpleFeatures
|
5
|
+
#Represents a point. It is in 3D if the Z coordinate is not +nil+.
|
6
|
+
class Point < Geometry
|
7
|
+
#Coordinates of the point
|
8
|
+
attr_accessor :x,:y,:z
|
9
|
+
|
10
|
+
def initialize(srid=DEFAULT_SRID)
|
11
|
+
super(srid)
|
12
|
+
@x=0.0
|
13
|
+
@y=0.0
|
14
|
+
@z=nil
|
15
|
+
end
|
16
|
+
#sets all coordinates in one call
|
17
|
+
def set_x_y_z(x,y,z)
|
18
|
+
@x=x
|
19
|
+
@y=y
|
20
|
+
@z=z
|
21
|
+
end
|
22
|
+
#sets all coordinates of a 2D point in one call
|
23
|
+
def set_x_y(x,y)
|
24
|
+
@x=x
|
25
|
+
@y=y
|
26
|
+
end
|
27
|
+
#tests the equality of points
|
28
|
+
def ==(other_point)
|
29
|
+
if other_point.class != self.class
|
30
|
+
false
|
31
|
+
else
|
32
|
+
@x == other_point.x and @y == other_point.y and @z == other_point.z
|
33
|
+
end
|
34
|
+
end
|
35
|
+
#binary representation of a point. It lacks some headers to be a valid EWKB representation.
|
36
|
+
def binary_representation(dimension=2)
|
37
|
+
if dimension == 2
|
38
|
+
[@x,@y].pack("EE")
|
39
|
+
else
|
40
|
+
[@x,@y,@z || 0].pack("EEE")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
#WKB geometry type of a point
|
44
|
+
def binary_geometry_type
|
45
|
+
1
|
46
|
+
end
|
47
|
+
|
48
|
+
#text representation of a point
|
49
|
+
def text_representation(dimension=2)
|
50
|
+
if dimension == 2
|
51
|
+
"#{@x} #{@y}"
|
52
|
+
else
|
53
|
+
"#{@x} #{@y} #{@z || 0}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
#WKT geometry type of a point
|
57
|
+
def text_geometry_type
|
58
|
+
"POINT"
|
59
|
+
end
|
60
|
+
|
61
|
+
#creates a point from an array of coordinates
|
62
|
+
def self.from_coordinates(coords,srid=DEFAULT_SRID)
|
63
|
+
point= Point::new(srid)
|
64
|
+
if coords.length == 2
|
65
|
+
point.set_x_y(*coords)
|
66
|
+
else
|
67
|
+
point.set_x_y_z(*coords)
|
68
|
+
end
|
69
|
+
point
|
70
|
+
end
|
71
|
+
#creates a point from the X and Y coordinates
|
72
|
+
def self.from_x_y(x,y,srid=DEFAULT_SRID)
|
73
|
+
point= Point::new(srid)
|
74
|
+
point.set_x_y(x,y)
|
75
|
+
point
|
76
|
+
end
|
77
|
+
#creates a point from the X, Y and Z coordinates
|
78
|
+
def self.from_x_y_z(x,y,z,srid=DEFAULT_SRID)
|
79
|
+
point= Point::new(srid)
|
80
|
+
point.set_x_y_z(x,y,z)
|
81
|
+
point
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'geo_ruby/simple_features/geometry'
|
2
|
+
|
3
|
+
module GeoRuby
|
4
|
+
module SimpleFeatures
|
5
|
+
#Represents a polygon as an array of linear rings (see LinearRing). No check is performed regarding the validity of the geometries forming the polygon.
|
6
|
+
class Polygon < Geometry
|
7
|
+
#the list of rings forming the polygon
|
8
|
+
attr_reader :rings
|
9
|
+
|
10
|
+
def initialize(srid = DEFAULT_SRID)
|
11
|
+
super(srid)
|
12
|
+
@rings = []
|
13
|
+
end
|
14
|
+
#add one to the polygon
|
15
|
+
def <<(ring)
|
16
|
+
@rings << ring
|
17
|
+
end
|
18
|
+
#add one or more rings to the polygon
|
19
|
+
def concat(rings)
|
20
|
+
@rings.concat rings
|
21
|
+
end
|
22
|
+
#number of linear rings in the polygon
|
23
|
+
def length
|
24
|
+
@rings.length
|
25
|
+
end
|
26
|
+
#access the nth linear ring
|
27
|
+
def [](n)
|
28
|
+
@rings[n]
|
29
|
+
end
|
30
|
+
#modifies the value of the nth linear ring
|
31
|
+
def []=(n,ring)
|
32
|
+
@rings[n]=ring
|
33
|
+
end
|
34
|
+
#iterates over the linear rings
|
35
|
+
def each(&proc)
|
36
|
+
@rings.each(&proc)
|
37
|
+
end
|
38
|
+
#iterates over the linear rings, passing their indices to the bloc
|
39
|
+
def each_index(&proc)
|
40
|
+
@rings.each_index(&proc)
|
41
|
+
end
|
42
|
+
#insert linear rings at the nth position
|
43
|
+
def insert(n,*ring)
|
44
|
+
@rings.insert(n,*ring)
|
45
|
+
end
|
46
|
+
#index of the linear_ring
|
47
|
+
def index(ring)
|
48
|
+
@rings.index(ring)
|
49
|
+
end
|
50
|
+
#remove linear rings. Arguments can be of the same type as Array#slice
|
51
|
+
def remove(*slice)
|
52
|
+
@rings.slice(*slice)
|
53
|
+
end
|
54
|
+
#tests for other equality. The SRID is not taken into account.
|
55
|
+
def ==(other_polygon)
|
56
|
+
if other_polygon.class != self.class or
|
57
|
+
length != other_polygon.length
|
58
|
+
false
|
59
|
+
else
|
60
|
+
index=0
|
61
|
+
while index<length
|
62
|
+
return false if self[index] != other_polygon[index]
|
63
|
+
index+=1
|
64
|
+
end
|
65
|
+
true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
#binary representation of a polygon, without the headers neccessary for a valid WKB string
|
69
|
+
def binary_representation(dimension=2)
|
70
|
+
rep = [length].pack("V")
|
71
|
+
each {|linear_ring| rep << linear_ring.binary_representation(dimension) }
|
72
|
+
rep
|
73
|
+
end
|
74
|
+
#WKB geometry type
|
75
|
+
def binary_geometry_type
|
76
|
+
3
|
77
|
+
end
|
78
|
+
#Text representation of a polygon
|
79
|
+
def text_representation(dimension=2)
|
80
|
+
@rings.collect{|line_string| "(" + line_string.text_representation(dimension) + ")" }.join(",")
|
81
|
+
end
|
82
|
+
#WKT geometry type
|
83
|
+
def text_geometry_type
|
84
|
+
"POLYGON"
|
85
|
+
end
|
86
|
+
#creates a new polygon. Accepts an array of linear strings as argument
|
87
|
+
def self.from_linear_rings(linear_rings,srid = DEFAULT_SRID)
|
88
|
+
polygon = Polygon::new(srid)
|
89
|
+
polygon.concat(linear_rings)
|
90
|
+
polygon
|
91
|
+
end
|
92
|
+
|
93
|
+
#creates a new polygon. Accepts a sequence of points as argument : ((x,y)....(x,y)),((x,y).....(x,y))
|
94
|
+
def self.from_raw_point_sequences(point_sequences,srid=DEFAULT_SRID)
|
95
|
+
polygon = Polygon::new(srid)
|
96
|
+
polygon.concat( point_sequences.collect {|point_sequence| LinearRing.from_raw_point_sequence(point_sequence,srid) } )
|
97
|
+
polygon
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/geo_ruby.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'geo_ruby/simple_features/geometry'
|
2
|
+
require 'geo_ruby/simple_features/point'
|
3
|
+
require 'geo_ruby/simple_features/line_string'
|
4
|
+
require 'geo_ruby/simple_features/linear_ring'
|
5
|
+
require 'geo_ruby/simple_features/polygon'
|
6
|
+
require 'geo_ruby/simple_features/multi_point'
|
7
|
+
require 'geo_ruby/simple_features/multi_line_string'
|
8
|
+
require 'geo_ruby/simple_features/multi_polygon'
|
9
|
+
require 'geo_ruby/simple_features/geometry_collection'
|
10
|
+
require 'geo_ruby/simple_features/ewkb_parser'
|
11
|
+
require 'geo_ruby/simple_features/geometry_factory'
|
12
|
+
|
data/rakefile.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc "Run the tests"
|
9
|
+
Rake::TestTask::new do |t|
|
10
|
+
t.test_files = FileList['test/test*.rb']
|
11
|
+
t.verbose = true
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "Generate the documentation"
|
15
|
+
Rake::RDocTask::new do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'doc/'
|
17
|
+
rdoc.title = "GeoRuby Documentation"
|
18
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
19
|
+
rdoc.rdoc_files.include('README')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
spec = Gem::Specification::new do |s|
|
24
|
+
s.platform = Gem::Platform::RUBY
|
25
|
+
|
26
|
+
s.name = 'GeoRuby'
|
27
|
+
s.version = "0.0.1"
|
28
|
+
s.summary = "Ruby data holder for OGC Simple Features"
|
29
|
+
s.description = <<EOF
|
30
|
+
GeoRuby is intended as a holder for data returned from PostGIS queries. Therefore, the data model roughly follows the OGC "Simple Features for SQL" specification (see www.opengis.org/docs/99-049.pdf), although without any kind of advanced functionalities (such as geometric operators or reprojections)
|
31
|
+
EOF
|
32
|
+
s.author = 'Guilhem Vellut'
|
33
|
+
s.email = 'guilhem.vellut+georuby@gmail.com'
|
34
|
+
s.homepage = "http://thepochisuperstarmegashow.com"
|
35
|
+
|
36
|
+
s.requirements << 'none'
|
37
|
+
s.require_path = 'lib'
|
38
|
+
s.files = FileList["lib/**/*.rb", "test/**/*.rb", "README","MIT-LICENSE","rakefile.rb"]
|
39
|
+
s.test_files = FileList['test/test*.rb']
|
40
|
+
|
41
|
+
s.has_rdoc = true
|
42
|
+
s.extra_rdoc_files = ["README"]
|
43
|
+
s.rdoc_options.concat ['--main', 'README']
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "Package the library as a gem"
|
47
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
48
|
+
pkg.need_zip = true
|
49
|
+
pkg.need_tar = true
|
50
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
|
3
|
+
require 'geo_ruby'
|
4
|
+
require 'test/unit'
|
5
|
+
|
6
|
+
include GeoRuby::SimpleFeatures
|
7
|
+
|
8
|
+
class TestEWKBParser < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@factory = GeometryFactory::new
|
12
|
+
@hex_ewkb_parser = HexEWKBParser::new(@factory)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_point2d
|
16
|
+
@hex_ewkb_parser.parse("01010000207B000000CDCCCCCCCCCC28406666666666A64640")
|
17
|
+
point = @factory.geometry
|
18
|
+
assert(point.instance_of?(Point))
|
19
|
+
assert_equal(Point.from_x_y(12.4,45.3,123),point)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_point3d
|
23
|
+
@hex_ewkb_parser.parse("01010000A07B000000CDCCCCCCCCCC28406666666666A646400000000000000CC0")
|
24
|
+
point = @factory.geometry
|
25
|
+
assert(point.instance_of?(Point))
|
26
|
+
assert_equal(Point.from_x_y_z(12.4,45.3,-3.5,123),point)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_line_string
|
30
|
+
@hex_ewkb_parser.parse("01020000200001000002000000CDCCCCCCCCCC28406666666666A646C03333333333B34640CDCCCCCCCCCC4440")
|
31
|
+
line_string = @factory.geometry
|
32
|
+
assert(line_string.instance_of?(LineString))
|
33
|
+
assert_equal(LineString.from_raw_point_sequence([[12.4,-45.3],[45.4,41.6]],256),line_string)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_polygon
|
37
|
+
@hex_ewkb_parser.parse("0103000020000100000200000005000000000000000000000000000000000000000000000000001040000000000000000000000000000010400000000000001040000000000000000000000000000010400000000000000000000000000000000005000000000000000000F03F000000000000F03F0000000000000840000000000000F03F00000000000008400000000000000840000000000000F03F0000000000000840000000000000F03F000000000000F03F")
|
38
|
+
polygon = @factory.geometry
|
39
|
+
assert(polygon.instance_of?(Polygon))
|
40
|
+
assert_equal(Polygon.from_raw_point_sequences([[[0,0],[4,0],[4,4],[0,4],[0,0]],[[1,1],[3,1],[3,3],[1,3],[1,1]]],256),polygon)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_geometry_collection
|
44
|
+
@hex_ewkb_parser.parse("010700002000010000020000000101000000AE47E17A14AE12403333333333B34640010200000002000000CDCCCCCCCCCC16406666666666E628403333333333E350400000000000004B40")
|
45
|
+
geometry_collection = @factory.geometry
|
46
|
+
assert(geometry_collection.instance_of?(GeometryCollection))
|
47
|
+
assert_equal(GeometryCollection.from_geometries([Point.from_x_y(4.67,45.4,256),LineString.from_raw_point_sequence([[5.7,12.45],[67.55,54]],256)],256),geometry_collection)
|
48
|
+
assert_equal(256,geometry_collection[0].srid)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_multi_point
|
52
|
+
@hex_ewkb_parser.parse("0104000020BC010000030000000101000000CDCCCCCCCCCC28403333333333D35EC0010100000066666666664650C09A99999999D95E4001010000001F97DD388EE35E400000000000C05E40")
|
53
|
+
multi_point = @factory.geometry
|
54
|
+
assert(multi_point.instance_of?(MultiPoint))
|
55
|
+
assert_equal(MultiPoint.from_raw_point_sequence([[12.4,-123.3],[-65.1,123.4],[123.55555555,123]],444),multi_point)
|
56
|
+
assert_equal(444,multi_point.srid)
|
57
|
+
assert_equal(444,multi_point[0].srid)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_multi_line_string
|
61
|
+
@hex_ewkb_parser.parse("01050000200001000002000000010200000002000000000000000000F83F9A99999999994640E4BD6A65C20F4BC0FA7E6ABC749388BF010200000003000000000000000000F83F9A99999999994640E4BD6A65C20F4BC0FA7E6ABC749388BF39B4C876BE8F46403333333333D35E40")
|
62
|
+
multi_line_string = @factory.geometry
|
63
|
+
assert(multi_line_string.instance_of?(MultiLineString))
|
64
|
+
assert_equal(MultiLineString.from_line_strings([LineString.from_raw_point_sequence([[1.5,45.2],[-54.12312,-0.012]],256),LineString.from_raw_point_sequence([[1.5,45.2],[-54.12312,-0.012],[45.123,123.3]],256)],256),multi_line_string)
|
65
|
+
assert_equal(256,multi_line_string.srid)
|
66
|
+
assert_equal(256,multi_line_string[0].srid)
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_multi_polygon
|
70
|
+
@hex_ewkb_parser.parse("0106000020000100000200000001030000000200000004000000CDCCCCCCCCCC28406666666666A646C03333333333B34640CDCCCCCCCCCC44406DE7FBA9F1D211403D2CD49AE61DF13FCDCCCCCCCCCC28406666666666A646C004000000333333333333034033333333333315409A999999999915408A8EE4F21FD2F63FEC51B81E85EB2C40F6285C8FC2F5F03F3333333333330340333333333333154001030000000200000005000000000000000000000000000000000000000000000000001040000000000000000000000000000010400000000000001040000000000000000000000000000010400000000000000000000000000000000005000000000000000000F03F000000000000F03F0000000000000840000000000000F03F00000000000008400000000000000840000000000000F03F0000000000000840000000000000F03F000000000000F03F")
|
71
|
+
multi_polygon = @factory.geometry
|
72
|
+
assert(multi_polygon.instance_of?(MultiPolygon))
|
73
|
+
assert_equal(MultiPolygon.from_polygons([Polygon.from_raw_point_sequences([[[12.4,-45.3],[45.4,41.6],[4.456,1.0698],[12.4,-45.3]],[[2.4,5.3],[5.4,1.4263],[14.46,1.06],[2.4,5.3]]],256),Polygon.from_raw_point_sequences([[[0,0],[4,0],[4,4],[0,4],[0,0]],[[1,1],[3,1],[3,3],[1,3],[1,1]]],256)],256),multi_polygon)
|
74
|
+
assert_equal(256,multi_polygon.srid)
|
75
|
+
assert_equal(256,multi_polygon[0].srid)
|
76
|
+
end
|
77
|
+
def test_failure_trailing_data
|
78
|
+
#added A345 at the end
|
79
|
+
assert_raise(StandardError){@hex_ewkb_parser.parse("01010000207B000000CDCCCCCCCCCC28406666666666A64640A345")}
|
80
|
+
end
|
81
|
+
def test_failure_unknown_geometry_type
|
82
|
+
assert_raise(StandardError){@hex_ewkb_parser.parse("01090000207B000000CDCCCCCCCCCC28406666666666A64640")}
|
83
|
+
end
|
84
|
+
def test_failure_m
|
85
|
+
assert_raise(StandardError){@hex_ewkb_parser.parse("01010000607B000000CDCCCCCCCCCC28406666666666A64640")}
|
86
|
+
end
|
87
|
+
def test_failure_truncated_data
|
88
|
+
assert_raise(StandardError){@hex_ewkb_parser.parse("01010000207B000000CDCCCCCCCCCC2840666666")}
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|