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