jsl-GeoRuby 1.3.3
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 +83 -0
- data/georuby.gemspec +37 -0
- data/lib/geo_ruby.rb +16 -0
- data/lib/geo_ruby/shp4r/dbf.rb +180 -0
- data/lib/geo_ruby/shp4r/shp.rb +701 -0
- data/lib/geo_ruby/simple_features/envelope.rb +134 -0
- data/lib/geo_ruby/simple_features/ewkb_parser.rb +216 -0
- data/lib/geo_ruby/simple_features/ewkt_parser.rb +336 -0
- data/lib/geo_ruby/simple_features/geometry.rb +225 -0
- data/lib/geo_ruby/simple_features/geometry_collection.rb +136 -0
- data/lib/geo_ruby/simple_features/geometry_factory.rb +81 -0
- data/lib/geo_ruby/simple_features/georss_parser.rb +135 -0
- data/lib/geo_ruby/simple_features/helper.rb +18 -0
- data/lib/geo_ruby/simple_features/line_string.rb +166 -0
- data/lib/geo_ruby/simple_features/linear_ring.rb +12 -0
- data/lib/geo_ruby/simple_features/multi_line_string.rb +39 -0
- data/lib/geo_ruby/simple_features/multi_point.rb +41 -0
- data/lib/geo_ruby/simple_features/multi_polygon.rb +38 -0
- data/lib/geo_ruby/simple_features/point.rb +236 -0
- data/lib/geo_ruby/simple_features/polygon.rb +150 -0
- data/rakefile.rb +44 -0
- data/test/data/multipoint.dbf +0 -0
- data/test/data/multipoint.shp +0 -0
- data/test/data/multipoint.shx +0 -0
- data/test/data/point.dbf +0 -0
- data/test/data/point.shp +0 -0
- data/test/data/point.shx +0 -0
- data/test/data/polygon.dbf +0 -0
- data/test/data/polygon.shp +0 -0
- data/test/data/polygon.shx +0 -0
- data/test/data/polyline.dbf +0 -0
- data/test/data/polyline.shp +0 -0
- data/test/data/polyline.shx +0 -0
- data/test/test_ewkb_parser.rb +171 -0
- data/test/test_ewkt_parser.rb +193 -0
- data/test/test_georss_kml.rb +231 -0
- data/test/test_shp.rb +76 -0
- data/test/test_shp_write.rb +150 -0
- data/test/test_simple_features.rb +506 -0
- data/tools/db.yml +6 -0
- data/tools/shp2sql.rb +92 -0
- metadata +95 -0
@@ -0,0 +1,134 @@
|
|
1
|
+
module GeoRuby
|
2
|
+
module SimpleFeatures
|
3
|
+
|
4
|
+
#Contains the bounding box of a geometry
|
5
|
+
class Envelope
|
6
|
+
attr_accessor :lower_corner, :upper_corner
|
7
|
+
attr_accessor :srid, :with_z
|
8
|
+
|
9
|
+
#Creates a enw Envelope with +lower_corner+ as the first element of the corners array and +upper_corner+ as the second element
|
10
|
+
def initialize(srid = DEFAULT_SRID, with_z = false)
|
11
|
+
@srid = srid
|
12
|
+
@with_z = with_z
|
13
|
+
end
|
14
|
+
|
15
|
+
#Merges the argument with the current evelope
|
16
|
+
def extend!(envelope)
|
17
|
+
lower_corner.x = [lower_corner.x,envelope.lower_corner.x].min
|
18
|
+
lower_corner.y = [lower_corner.y,envelope.lower_corner.y].min
|
19
|
+
upper_corner.x = [upper_corner.x,envelope.upper_corner.x].max
|
20
|
+
upper_corner.y = [upper_corner.y,envelope.upper_corner.y].max
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
#Merges the argument with the current evelope and sends back a new
|
25
|
+
#envelope without changing the current one
|
26
|
+
def extend(envelope)
|
27
|
+
e = Envelope.from_points([Point.from_x_y(lower_corner.x,lower_corner.y),
|
28
|
+
Point.from_x_y(upper_corner.x,upper_corner.y)],srid,with_z)
|
29
|
+
e.extend!(envelope)
|
30
|
+
e
|
31
|
+
end
|
32
|
+
|
33
|
+
#Sends back the center of the envelope
|
34
|
+
def center
|
35
|
+
Point.from_x_y((lower_corner.x + upper_corner.x)/2,(lower_corner.y + upper_corner.y)/2)
|
36
|
+
end
|
37
|
+
|
38
|
+
#Tests the equality of line strings
|
39
|
+
def ==(other_envelope)
|
40
|
+
if other_envelope.class != self.class
|
41
|
+
false
|
42
|
+
else
|
43
|
+
upper_corner == other_envelope.upper_corner and lower_corner == other_envelope.lower_corner
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
#georss serialization: Dialect can be passed as option <tt>:dialect</tt> and set to <tt>:simple</tt> (default)
|
48
|
+
#<tt>:w3cgeo</tt> or <tt>:gml</tt>. Options <tt>:featuretypetag
|
49
|
+
def as_georss(options = {})
|
50
|
+
dialect= options[:dialect] || :simple
|
51
|
+
case(dialect)
|
52
|
+
when :simple
|
53
|
+
geom_attr = ""
|
54
|
+
geom_attr += " featuretypetag=\"#{options[:featuretypetag]}\"" if options[:featuretypetag]
|
55
|
+
geom_attr += " relationshiptag=\"#{options[:relationshiptag]}\"" if options[:relationshiptag]
|
56
|
+
geom_attr += " floor=\"#{options[:floor]}\"" if options[:floor]
|
57
|
+
geom_attr += " radius=\"#{options[:radius]}\"" if options[:radius]
|
58
|
+
geom_attr += " elev=\"#{options[:elev]}\"" if options[:elev]
|
59
|
+
|
60
|
+
georss_simple_representation(options.merge(:geom_attr => geom_attr))
|
61
|
+
when :w3cgeo
|
62
|
+
georss_w3cgeo_representation(options)
|
63
|
+
when :gml
|
64
|
+
georss_gml_representation(options)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
#georss simple representation
|
69
|
+
def georss_simple_representation(options = {}) #:nodoc:
|
70
|
+
georss_ns = options[:georss_ns] || "georss"
|
71
|
+
geom_attr = options[:geom_attr]
|
72
|
+
"<#{georss_ns}:box#{geom_attr}>#{lower_corner.y} #{lower_corner.x} #{upper_corner.y} #{upper_corner.x}</#{georss_ns}:box>\n"
|
73
|
+
end
|
74
|
+
|
75
|
+
#georss w3c representation : outputs the first point of the line
|
76
|
+
def georss_w3cgeo_representation(options = {}) #:nodoc:
|
77
|
+
w3cgeo_ns = options[:w3cgeo_ns] || "geo"
|
78
|
+
point = self.center
|
79
|
+
"<#{w3cgeo_ns}:lat>#{point.y}</#{w3cgeo_ns}:lat>\n<#{w3cgeo_ns}:long>#{point.x}</#{w3cgeo_ns}:long>\n"
|
80
|
+
end
|
81
|
+
|
82
|
+
#georss gml representation
|
83
|
+
def georss_gml_representation(options = {}) #:nodoc:
|
84
|
+
georss_ns = options[:georss_ns] || "georss"
|
85
|
+
gml_ns = options[:gml_ns] || "gml"
|
86
|
+
result = "<#{georss_ns}:where>\n<#{gml_ns}:Envelope>\n"
|
87
|
+
result += "<#{gml_ns}:LowerCorner>" + "#{lower_corner.y} #{lower_corner.x}" + "</#{gml_ns}:LowerCorner>"
|
88
|
+
result += "<#{gml_ns}:UpperCorner>" + "#{upper_corner.y} #{upper_corner.x}" + "</#{gml_ns}:UpperCorner>"
|
89
|
+
result += "</#{gml_ns}:Envelope>\n</#{georss_ns}:where>\n"
|
90
|
+
end
|
91
|
+
|
92
|
+
#Sends back a latlonaltbox
|
93
|
+
def as_kml(options = {})
|
94
|
+
geom_data = ""
|
95
|
+
geom_data = "<altitudeMode>#{options[:altitude_mode]}</altitudeMode>\n" if options[:altitude_mode]
|
96
|
+
|
97
|
+
allow_z = with_z && (!options[:altitude_mode].nil?) && options[:atitude_mode] != "clampToGround"
|
98
|
+
|
99
|
+
kml_representation(options.merge(:geom_data => geom_data,:allow_z => allow_z))
|
100
|
+
end
|
101
|
+
|
102
|
+
def kml_representation(options = {})#:nodoc:
|
103
|
+
result = "<LatLonAltBox>\n"
|
104
|
+
result += options[:geom_data]
|
105
|
+
result += "<north>#{upper_corner.y}</north>\n"
|
106
|
+
result += "<south>#{lower_corner.y}</south>\n"
|
107
|
+
result += "<east>#{upper_corner.x}</east>\n"
|
108
|
+
result += "<west>#{lower_corner.x}</west>\n"
|
109
|
+
|
110
|
+
if with_z
|
111
|
+
result += "<minAltitude>#{lower_corner.z}</minAltitude>"
|
112
|
+
result += "<maxAltitude>#{upper_corner.z}</maxAltitude>"
|
113
|
+
end
|
114
|
+
|
115
|
+
result += "</LatLonAltBox>\n"
|
116
|
+
end
|
117
|
+
|
118
|
+
#Creates a new envelope. Accept an array of 2 points as argument
|
119
|
+
def self.from_points(points,srid=DEFAULT_SRID,with_z=false)
|
120
|
+
e = Envelope.new(srid,with_z)
|
121
|
+
e.lower_corner, e.upper_corner = points
|
122
|
+
e
|
123
|
+
end
|
124
|
+
|
125
|
+
#Creates a new envelope. Accept a sequence of point coordinates as argument : ((x,y),(x,y))
|
126
|
+
def self.from_coordinates(points,srid=DEFAULT_SRID,with_z=false)
|
127
|
+
e = Envelope.new(srid,with_z)
|
128
|
+
e.lower_corner, e.upper_corner = points.collect{|point_coords| Point.from_coordinates(point_coords,srid,with_z)}
|
129
|
+
e
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,216 @@
|
|
1
|
+
require 'geo_ruby/simple_features/point'
|
2
|
+
require 'geo_ruby/simple_features/line_string'
|
3
|
+
require 'geo_ruby/simple_features/linear_ring'
|
4
|
+
require 'geo_ruby/simple_features/polygon'
|
5
|
+
require 'geo_ruby/simple_features/multi_point'
|
6
|
+
require 'geo_ruby/simple_features/multi_line_string'
|
7
|
+
require 'geo_ruby/simple_features/multi_polygon'
|
8
|
+
require 'geo_ruby/simple_features/geometry_collection'
|
9
|
+
|
10
|
+
module GeoRuby
|
11
|
+
module SimpleFeatures
|
12
|
+
|
13
|
+
#Raised when an error in the EWKB string is detected
|
14
|
+
class EWKBFormatError < StandardError
|
15
|
+
end
|
16
|
+
|
17
|
+
#Parses EWKB strings and notifies of events (such as the beginning of the definition of geometry, the value of the SRID...) the factory passed as argument to the constructor.
|
18
|
+
#
|
19
|
+
#=Example
|
20
|
+
# factory = GeometryFactory::new
|
21
|
+
# ewkb_parser = EWKBParser::new(factory)
|
22
|
+
# ewkb_parser.parse(<EWKB String>)
|
23
|
+
# geometry = @factory.geometry
|
24
|
+
#
|
25
|
+
#You can also use directly the static method Geometry.from_ewkb
|
26
|
+
class EWKBParser
|
27
|
+
|
28
|
+
def initialize(factory)
|
29
|
+
@factory = factory
|
30
|
+
@parse_options ={
|
31
|
+
1 => method(:parse_point),
|
32
|
+
2 => method(:parse_line_string),
|
33
|
+
3 => method(:parse_polygon),
|
34
|
+
4 => method(:parse_multi_point),
|
35
|
+
5 => method(:parse_multi_line_string),
|
36
|
+
6 => method(:parse_multi_polygon),
|
37
|
+
7 => method(:parse_geometry_collection)
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
#Parses the ewkb string passed as argument and notifies the factory of events
|
42
|
+
def parse(ewkb)
|
43
|
+
@factory.reset
|
44
|
+
@unpack_structure=UnpackStructure::new(ewkb)
|
45
|
+
@with_z = false
|
46
|
+
@with_m = false
|
47
|
+
parse_geometry
|
48
|
+
@unpack_structure.done
|
49
|
+
@srid=nil
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def parse_geometry
|
54
|
+
@unpack_structure.endianness=@unpack_structure.read_byte
|
55
|
+
@geometry_type = @unpack_structure.read_uint
|
56
|
+
|
57
|
+
if (@geometry_type & Z_MASK) != 0
|
58
|
+
@with_z=true
|
59
|
+
@geometry_type = @geometry_type & ~Z_MASK
|
60
|
+
end
|
61
|
+
if (@geometry_type & M_MASK) != 0
|
62
|
+
@with_m=true
|
63
|
+
@geometry_type = @geometry_type & ~M_MASK
|
64
|
+
end
|
65
|
+
if (@geometry_type & SRID_MASK) != 0
|
66
|
+
@srid = @unpack_structure.read_uint
|
67
|
+
@geometry_type = @geometry_type & ~SRID_MASK
|
68
|
+
else
|
69
|
+
#to manage multi geometries : the srid is not present in sub_geometries, therefore we take the srid of the parent ; if it is the root, we take the default srid
|
70
|
+
@srid= @srid || DEFAULT_SRID
|
71
|
+
end
|
72
|
+
|
73
|
+
if @parse_options.has_key? @geometry_type
|
74
|
+
@parse_options[@geometry_type].call
|
75
|
+
else
|
76
|
+
raise EWKBFormatError::new("Unknown geometry type")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def parse_geometry_collection
|
81
|
+
parse_multi_geometries(GeometryCollection)
|
82
|
+
end
|
83
|
+
|
84
|
+
def parse_multi_polygon
|
85
|
+
parse_multi_geometries(MultiPolygon)
|
86
|
+
end
|
87
|
+
|
88
|
+
def parse_multi_line_string
|
89
|
+
parse_multi_geometries(MultiLineString)
|
90
|
+
end
|
91
|
+
|
92
|
+
def parse_multi_point
|
93
|
+
parse_multi_geometries(MultiPoint)
|
94
|
+
end
|
95
|
+
|
96
|
+
def parse_multi_geometries(geometry_type)
|
97
|
+
@factory.begin_geometry(geometry_type,@srid)
|
98
|
+
num_geometries = @unpack_structure.read_uint
|
99
|
+
1.upto(num_geometries) { parse_geometry }
|
100
|
+
@factory.end_geometry(@with_z,@with_m)
|
101
|
+
end
|
102
|
+
|
103
|
+
def parse_polygon
|
104
|
+
@factory.begin_geometry(Polygon,@srid)
|
105
|
+
num_linear_rings = @unpack_structure.read_uint
|
106
|
+
1.upto(num_linear_rings) {parse_linear_ring}
|
107
|
+
@factory.end_geometry(@with_z,@with_m)
|
108
|
+
end
|
109
|
+
|
110
|
+
def parse_linear_ring
|
111
|
+
parse_point_list(LinearRing)
|
112
|
+
end
|
113
|
+
|
114
|
+
def parse_line_string
|
115
|
+
parse_point_list(LineString)
|
116
|
+
end
|
117
|
+
|
118
|
+
#used to parse line_strings and linear_rings
|
119
|
+
def parse_point_list(geometry_type)
|
120
|
+
@factory.begin_geometry(geometry_type,@srid)
|
121
|
+
num_points = @unpack_structure.read_uint
|
122
|
+
1.upto(num_points) {parse_point}
|
123
|
+
@factory.end_geometry(@with_z,@with_m)
|
124
|
+
end
|
125
|
+
|
126
|
+
def parse_point
|
127
|
+
@factory.begin_geometry(Point,@srid)
|
128
|
+
x = @unpack_structure.read_double
|
129
|
+
y = @unpack_structure.read_double
|
130
|
+
if ! (@with_z or @with_m) #most common case probably
|
131
|
+
@factory.add_point_x_y(x,y)
|
132
|
+
elsif @with_m and @with_z
|
133
|
+
z = @unpack_structure.read_double
|
134
|
+
m = @unpack_structure.read_double
|
135
|
+
@factory.add_point_x_y_z_m(x,y,z,m)
|
136
|
+
elsif @with_z
|
137
|
+
z = @unpack_structure.read_double
|
138
|
+
@factory.add_point_x_y_z(x,y,z)
|
139
|
+
else
|
140
|
+
m = @unpack_structure.read_double
|
141
|
+
@factory.add_point_x_y_m(x,y,m)
|
142
|
+
end
|
143
|
+
|
144
|
+
@factory.end_geometry(@with_z,@with_m)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
#Parses HexEWKB strings. In reality, it just transforms the HexEWKB string into the equivalent EWKB string and lets the EWKBParser do the actual parsing.
|
149
|
+
class HexEWKBParser < EWKBParser
|
150
|
+
def initialize(factory)
|
151
|
+
super(factory)
|
152
|
+
end
|
153
|
+
#parses an HexEWKB string
|
154
|
+
def parse(hexewkb)
|
155
|
+
super(decode_hex(hexewkb))
|
156
|
+
end
|
157
|
+
#transforms a HexEWKB string into an EWKB string
|
158
|
+
def decode_hex(hexewkb)
|
159
|
+
result=""
|
160
|
+
num_bytes = (hexewkb.size + 1) / 2
|
161
|
+
0.upto(num_bytes-1) do |i|
|
162
|
+
result << hexewkb[i*2,2].hex
|
163
|
+
end
|
164
|
+
result
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
class HexEWKBParser < EWKBParser
|
169
|
+
#transforms a HexEWKB string into an EWKB string
|
170
|
+
# Patched for speedup
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
class UnpackStructure #:nodoc:
|
175
|
+
NDR=1
|
176
|
+
XDR=0
|
177
|
+
def initialize(ewkb)
|
178
|
+
@position=0
|
179
|
+
@ewkb=ewkb
|
180
|
+
end
|
181
|
+
def done
|
182
|
+
raise EWKBFormatError::new("Trailing data") if @position != @ewkb.length
|
183
|
+
end
|
184
|
+
def read_double
|
185
|
+
i=@position
|
186
|
+
@position += 8
|
187
|
+
packed_double = @ewkb[i...@position]
|
188
|
+
raise EWKBFormatError::new("Truncated data") if packed_double.nil? or packed_double.length < 8
|
189
|
+
packed_double.unpack(@double_mark)[0]
|
190
|
+
end
|
191
|
+
def read_uint
|
192
|
+
i=@position
|
193
|
+
@position += 4
|
194
|
+
packed_uint = @ewkb[i...@position]
|
195
|
+
raise EWKBFormatError::new("Truncated data") if packed_uint.nil? or packed_uint.length < 4
|
196
|
+
packed_uint.unpack(@uint_mark)[0]
|
197
|
+
end
|
198
|
+
def read_byte
|
199
|
+
i = @position
|
200
|
+
@position += 1
|
201
|
+
packed_byte = @ewkb[i...@position]
|
202
|
+
raise EWKBFormatError::new("Truncated data") if packed_byte.nil? or packed_byte.length < 1
|
203
|
+
packed_byte.unpack("C")[0]
|
204
|
+
end
|
205
|
+
def endianness=(byte_order)
|
206
|
+
if(byte_order == NDR)
|
207
|
+
@uint_mark="V"
|
208
|
+
@double_mark="E"
|
209
|
+
elsif(byte_order == XDR)
|
210
|
+
@uint_mark="N"
|
211
|
+
@double_mark="G"
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
@@ -0,0 +1,336 @@
|
|
1
|
+
require 'geo_ruby/simple_features/point'
|
2
|
+
require 'geo_ruby/simple_features/line_string'
|
3
|
+
require 'geo_ruby/simple_features/linear_ring'
|
4
|
+
require 'geo_ruby/simple_features/polygon'
|
5
|
+
require 'geo_ruby/simple_features/multi_point'
|
6
|
+
require 'geo_ruby/simple_features/multi_line_string'
|
7
|
+
require 'geo_ruby/simple_features/multi_polygon'
|
8
|
+
require 'geo_ruby/simple_features/geometry_collection'
|
9
|
+
|
10
|
+
require 'strscan'
|
11
|
+
|
12
|
+
module GeoRuby
|
13
|
+
module SimpleFeatures
|
14
|
+
|
15
|
+
#Raised when an error in the EWKT string is detected
|
16
|
+
class EWKTFormatError < StandardError
|
17
|
+
end
|
18
|
+
|
19
|
+
#Parses EWKT strings and notifies of events (such as the beginning of the definition of geometry, the value of the SRID...) the factory passed as argument to the constructor.
|
20
|
+
#
|
21
|
+
#=Example
|
22
|
+
# factory = GeometryFactory::new
|
23
|
+
# ewkt_parser = EWKTParser::new(factory)
|
24
|
+
# ewkt_parser.parse(<EWKT String>)
|
25
|
+
# geometry = @factory.geometry
|
26
|
+
#
|
27
|
+
#You can also use directly the static method Geometry.from_ewkt
|
28
|
+
class EWKTParser
|
29
|
+
|
30
|
+
def initialize(factory)
|
31
|
+
@factory = factory
|
32
|
+
@parse_options ={
|
33
|
+
"POINT" => method(:parse_point),
|
34
|
+
"LINESTRING" => method(:parse_line_string),
|
35
|
+
"POLYGON" => method(:parse_polygon),
|
36
|
+
"MULTIPOINT" => method(:parse_multi_point),
|
37
|
+
"MULTILINESTRING" => method(:parse_multi_line_string),
|
38
|
+
"MULTIPOLYGON" => method(:parse_multi_polygon),
|
39
|
+
"GEOMETRYCOLLECTION" => method(:parse_geometry_collection)
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
#Parses the ewkt string passed as argument and notifies the factory of events
|
44
|
+
def parse(ewkt)
|
45
|
+
@factory.reset
|
46
|
+
@tokenizer_structure = TokenizerStructure.new(ewkt)
|
47
|
+
@with_z=false
|
48
|
+
@with_m=false
|
49
|
+
@is_3dm = false
|
50
|
+
parse_geometry(true)
|
51
|
+
@srid=nil
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def parse_geometry(srid_allowed)
|
56
|
+
|
57
|
+
token = @tokenizer_structure.get_next_token
|
58
|
+
if token == 'SRID'
|
59
|
+
#SRID present
|
60
|
+
raise EWKTFormatError.new("SRID not allowed at this position") if(!srid_allowed)
|
61
|
+
if @tokenizer_structure.get_next_token != '='
|
62
|
+
raise EWKTFormatError.new("Invalid SRID expression")
|
63
|
+
else
|
64
|
+
@srid = @tokenizer_structure.get_next_token.to_i
|
65
|
+
raise EWKTFormatError.new("Invalid SRID separator") if @tokenizer_structure.get_next_token != ';'
|
66
|
+
geom_type = @tokenizer_structure.get_next_token
|
67
|
+
end
|
68
|
+
|
69
|
+
else
|
70
|
+
#to manage multi geometries : the srid is not present in sub_geometries, therefore we take the srid of the parent ; if it is the root, we take the default srid
|
71
|
+
@srid= @srid || DEFAULT_SRID
|
72
|
+
geom_type = token
|
73
|
+
end
|
74
|
+
|
75
|
+
if geom_type[-1] == ?M
|
76
|
+
@is_3dm=true
|
77
|
+
@with_m=true
|
78
|
+
geom_type.chop! #remove the M
|
79
|
+
end
|
80
|
+
|
81
|
+
if @parse_options.has_key?(geom_type)
|
82
|
+
@parse_options[geom_type].call
|
83
|
+
else
|
84
|
+
raise EWKTFormatError.new("Urecognized geometry type: #{geom_type}")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def parse_geometry_collection
|
89
|
+
if @tokenizer_structure.get_next_token !='('
|
90
|
+
raise EWKTFormatError.new('Invalid GeometryCollection')
|
91
|
+
end
|
92
|
+
|
93
|
+
@factory.begin_geometry(GeometryCollection,@srid)
|
94
|
+
|
95
|
+
token = ''
|
96
|
+
while token != ')'
|
97
|
+
parse_geometry(false)
|
98
|
+
token = @tokenizer_structure.get_next_token
|
99
|
+
if token.nil?
|
100
|
+
raise EWKTFormatError.new("EWKT string not correctly terminated")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
@factory.end_geometry(@with_z,@with_m)
|
105
|
+
end
|
106
|
+
|
107
|
+
def parse_multi_polygon
|
108
|
+
if @tokenizer_structure.get_next_token !='('
|
109
|
+
raise EWKTFormatError.new('Invalid MultiLineString')
|
110
|
+
end
|
111
|
+
|
112
|
+
@factory.begin_geometry(MultiPolygon,@srid)
|
113
|
+
token = ''
|
114
|
+
while token != ')'
|
115
|
+
parse_polygon
|
116
|
+
token = @tokenizer_structure.get_next_token
|
117
|
+
if token.nil?
|
118
|
+
raise EWKTFormatError.new("EWKT string not correctly terminated")
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
@factory.end_geometry(@with_z,@with_m)
|
123
|
+
end
|
124
|
+
|
125
|
+
def parse_multi_line_string
|
126
|
+
if @tokenizer_structure.get_next_token !='('
|
127
|
+
raise EWKTFormatError.new('Invalid MultiLineString')
|
128
|
+
end
|
129
|
+
|
130
|
+
@factory.begin_geometry(MultiLineString,@srid)
|
131
|
+
|
132
|
+
token = ''
|
133
|
+
while token != ')'
|
134
|
+
parse_line_string
|
135
|
+
token = @tokenizer_structure.get_next_token
|
136
|
+
if token.nil?
|
137
|
+
raise EWKTFormatError.new("EWKT string not correctly terminated")
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
@factory.end_geometry(@with_z,@with_m)
|
142
|
+
end
|
143
|
+
|
144
|
+
def parse_polygon
|
145
|
+
if @tokenizer_structure.get_next_token !='('
|
146
|
+
raise EWKTFormatError.new('Invalid Polygon')
|
147
|
+
end
|
148
|
+
|
149
|
+
@factory.begin_geometry(Polygon,@srid)
|
150
|
+
|
151
|
+
token = ''
|
152
|
+
while token != ')'
|
153
|
+
parse_linear_ring
|
154
|
+
token = @tokenizer_structure.get_next_token
|
155
|
+
if token.nil?
|
156
|
+
raise EWKTFormatError.new("EWKT string not correctly terminated")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
@factory.end_geometry(@with_z,@with_m)
|
161
|
+
end
|
162
|
+
|
163
|
+
#must support the PostGIS form and the one in the specification
|
164
|
+
def parse_multi_point
|
165
|
+
if @tokenizer_structure.get_next_token !='('
|
166
|
+
raise EWKTFormatError.new('Invalid MultiPoint')
|
167
|
+
end
|
168
|
+
|
169
|
+
token = @tokenizer_structure.check_next_token
|
170
|
+
if token == '('
|
171
|
+
#specification
|
172
|
+
@factory.begin_geometry(MultiPoint,@srid)
|
173
|
+
|
174
|
+
token = ''
|
175
|
+
while token != ')'
|
176
|
+
parse_point
|
177
|
+
token = @tokenizer_structure.get_next_token
|
178
|
+
if token.nil?
|
179
|
+
raise EWKTFormatError.new("EWKT string not correctly terminated")
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
@factory.end_geometry(@with_z,@with_m)
|
184
|
+
else
|
185
|
+
#postgis
|
186
|
+
parse_point_list(MultiPoint)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def parse_linear_ring
|
191
|
+
if @tokenizer_structure.get_next_token !='('
|
192
|
+
raise EWKTFormatError.new('Invalid Linear ring')
|
193
|
+
end
|
194
|
+
|
195
|
+
parse_point_list(LinearRing)
|
196
|
+
end
|
197
|
+
|
198
|
+
def parse_line_string
|
199
|
+
if @tokenizer_structure.get_next_token !='('
|
200
|
+
raise EWKTFormatError.new('Invalid Line string')
|
201
|
+
end
|
202
|
+
|
203
|
+
parse_point_list(LineString)
|
204
|
+
end
|
205
|
+
|
206
|
+
#used to parse line_strings and linear_rings and the PostGIS form of multi_points
|
207
|
+
def parse_point_list(geometry_type)
|
208
|
+
@factory.begin_geometry(geometry_type,@srid)
|
209
|
+
|
210
|
+
token = ''
|
211
|
+
while token != ')'
|
212
|
+
@factory.begin_geometry(Point,@srid)
|
213
|
+
token = parse_coords
|
214
|
+
if token.nil?
|
215
|
+
raise EWKTFormatError.new("EWKT string not correctly terminated")
|
216
|
+
end
|
217
|
+
@factory.end_geometry(@with_z,@with_m)
|
218
|
+
end
|
219
|
+
|
220
|
+
@factory.end_geometry(@with_z,@with_m)
|
221
|
+
end
|
222
|
+
|
223
|
+
def parse_point
|
224
|
+
if @tokenizer_structure.get_next_token !='('
|
225
|
+
raise EWKTFormatError.new('Invalid Point')
|
226
|
+
end
|
227
|
+
|
228
|
+
@factory.begin_geometry(Point,@srid)
|
229
|
+
|
230
|
+
token = parse_coords
|
231
|
+
|
232
|
+
if token != ')'
|
233
|
+
raise EWKTFormatError.new("EWKT string not correctly terminated")
|
234
|
+
end
|
235
|
+
|
236
|
+
@factory.end_geometry(@with_z,@with_m)
|
237
|
+
end
|
238
|
+
|
239
|
+
def parse_coords
|
240
|
+
coords = Array.new
|
241
|
+
x = @tokenizer_structure.get_next_token
|
242
|
+
y = @tokenizer_structure.get_next_token
|
243
|
+
|
244
|
+
if x.nil? or y.nil?
|
245
|
+
raise EWKTFormatError.new("Bad Point format")
|
246
|
+
end
|
247
|
+
|
248
|
+
if @is_3dm
|
249
|
+
m = @tokenizer_structure.get_next_token
|
250
|
+
|
251
|
+
if m.nil? or m == ',' or m == ')'
|
252
|
+
raise EWKTFormatError.new("No M dimension found")
|
253
|
+
else
|
254
|
+
@factory.add_point_x_y_m(x.to_f,y.to_f,m.to_f)
|
255
|
+
@tokenizer_structure.get_next_token
|
256
|
+
end
|
257
|
+
else
|
258
|
+
z = @tokenizer_structure.get_next_token
|
259
|
+
|
260
|
+
if z.nil?
|
261
|
+
raise EWKTFormatError.new("EWKT string not correctly terminated")
|
262
|
+
end
|
263
|
+
|
264
|
+
if z == ',' or z == ')'
|
265
|
+
#2D : no z no m
|
266
|
+
@factory.add_point_x_y(x.to_f,y.to_f)
|
267
|
+
z
|
268
|
+
else
|
269
|
+
m = @tokenizer_structure.get_next_token
|
270
|
+
if m.nil?
|
271
|
+
raise EWKTFormatError.new("EWKT string not correctly terminated")
|
272
|
+
end
|
273
|
+
|
274
|
+
if m == ',' or m ==')'
|
275
|
+
#3Dz : no m
|
276
|
+
@with_z = true
|
277
|
+
@factory.add_point_x_y_z(x.to_f,y.to_f,z.to_f)
|
278
|
+
m
|
279
|
+
else
|
280
|
+
#4D
|
281
|
+
@with_z = true
|
282
|
+
@with_m = true
|
283
|
+
@factory.add_point_x_y_z_m(x.to_f,y.to_f,z.to_f,m.to_f)
|
284
|
+
@tokenizer_structure.get_next_token
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
class TokenizerStructure
|
292
|
+
|
293
|
+
def initialize(ewkt)
|
294
|
+
@ewkt = ewkt
|
295
|
+
@scanner = StringScanner.new(ewkt)
|
296
|
+
@regex = /\s*([\w.-]+)s*/
|
297
|
+
end
|
298
|
+
|
299
|
+
def get_next_token
|
300
|
+
if @scanner.scan(@regex).nil?
|
301
|
+
if @scanner.eos?
|
302
|
+
nil
|
303
|
+
else
|
304
|
+
ch = @scanner.getch
|
305
|
+
while ch == ' '
|
306
|
+
ch = @scanner.getch
|
307
|
+
end
|
308
|
+
ch
|
309
|
+
end
|
310
|
+
else
|
311
|
+
@scanner[1]
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
|
316
|
+
def check_next_token
|
317
|
+
check = @scanner.check(@regex)
|
318
|
+
if check.nil?
|
319
|
+
if @scanner.eos?
|
320
|
+
nil
|
321
|
+
else
|
322
|
+
pos = @scanner.pos
|
323
|
+
while @ewkt[pos].chr == ' '
|
324
|
+
pos+=1
|
325
|
+
end
|
326
|
+
@ewkt[pos].chr
|
327
|
+
end
|
328
|
+
else
|
329
|
+
check
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
end
|
334
|
+
|
335
|
+
end
|
336
|
+
end
|