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.
Files changed (43) hide show
  1. data/MIT-LICENSE +7 -0
  2. data/README +83 -0
  3. data/georuby.gemspec +37 -0
  4. data/lib/geo_ruby.rb +16 -0
  5. data/lib/geo_ruby/shp4r/dbf.rb +180 -0
  6. data/lib/geo_ruby/shp4r/shp.rb +701 -0
  7. data/lib/geo_ruby/simple_features/envelope.rb +134 -0
  8. data/lib/geo_ruby/simple_features/ewkb_parser.rb +216 -0
  9. data/lib/geo_ruby/simple_features/ewkt_parser.rb +336 -0
  10. data/lib/geo_ruby/simple_features/geometry.rb +225 -0
  11. data/lib/geo_ruby/simple_features/geometry_collection.rb +136 -0
  12. data/lib/geo_ruby/simple_features/geometry_factory.rb +81 -0
  13. data/lib/geo_ruby/simple_features/georss_parser.rb +135 -0
  14. data/lib/geo_ruby/simple_features/helper.rb +18 -0
  15. data/lib/geo_ruby/simple_features/line_string.rb +166 -0
  16. data/lib/geo_ruby/simple_features/linear_ring.rb +12 -0
  17. data/lib/geo_ruby/simple_features/multi_line_string.rb +39 -0
  18. data/lib/geo_ruby/simple_features/multi_point.rb +41 -0
  19. data/lib/geo_ruby/simple_features/multi_polygon.rb +38 -0
  20. data/lib/geo_ruby/simple_features/point.rb +236 -0
  21. data/lib/geo_ruby/simple_features/polygon.rb +150 -0
  22. data/rakefile.rb +44 -0
  23. data/test/data/multipoint.dbf +0 -0
  24. data/test/data/multipoint.shp +0 -0
  25. data/test/data/multipoint.shx +0 -0
  26. data/test/data/point.dbf +0 -0
  27. data/test/data/point.shp +0 -0
  28. data/test/data/point.shx +0 -0
  29. data/test/data/polygon.dbf +0 -0
  30. data/test/data/polygon.shp +0 -0
  31. data/test/data/polygon.shx +0 -0
  32. data/test/data/polyline.dbf +0 -0
  33. data/test/data/polyline.shp +0 -0
  34. data/test/data/polyline.shx +0 -0
  35. data/test/test_ewkb_parser.rb +171 -0
  36. data/test/test_ewkt_parser.rb +193 -0
  37. data/test/test_georss_kml.rb +231 -0
  38. data/test/test_shp.rb +76 -0
  39. data/test/test_shp_write.rb +150 -0
  40. data/test/test_simple_features.rb +506 -0
  41. data/tools/db.yml +6 -0
  42. data/tools/shp2sql.rb +92 -0
  43. 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