ppe-georuby 1.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/.gitignore +6 -0
  2. data/History.txt +4 -0
  3. data/LICENSE +21 -0
  4. data/README.txt +118 -0
  5. data/Rakefile +48 -0
  6. data/VERSION +1 -0
  7. data/lib/geo_ruby.rb +22 -0
  8. data/lib/geo_ruby/gpx.rb +1 -0
  9. data/lib/geo_ruby/gpx4r/gpx.rb +117 -0
  10. data/lib/geo_ruby/shp.rb +1 -0
  11. data/lib/geo_ruby/shp4r/dbf.rb +41 -0
  12. data/lib/geo_ruby/shp4r/shp.rb +697 -0
  13. data/lib/geo_ruby/simple_features/envelope.rb +167 -0
  14. data/lib/geo_ruby/simple_features/ewkb_parser.rb +216 -0
  15. data/lib/geo_ruby/simple_features/ewkt_parser.rb +336 -0
  16. data/lib/geo_ruby/simple_features/geometry.rb +228 -0
  17. data/lib/geo_ruby/simple_features/geometry_collection.rb +136 -0
  18. data/lib/geo_ruby/simple_features/geometry_factory.rb +81 -0
  19. data/lib/geo_ruby/simple_features/georss_parser.rb +135 -0
  20. data/lib/geo_ruby/simple_features/helper.rb +18 -0
  21. data/lib/geo_ruby/simple_features/line_string.rb +206 -0
  22. data/lib/geo_ruby/simple_features/linear_ring.rb +12 -0
  23. data/lib/geo_ruby/simple_features/multi_line_string.rb +51 -0
  24. data/lib/geo_ruby/simple_features/multi_point.rb +46 -0
  25. data/lib/geo_ruby/simple_features/multi_polygon.rb +52 -0
  26. data/lib/geo_ruby/simple_features/point.rb +364 -0
  27. data/lib/geo_ruby/simple_features/polygon.rb +157 -0
  28. data/ppe-georuby.gemspec +133 -0
  29. data/script/console +10 -0
  30. data/script/destroy +14 -0
  31. data/script/generate +14 -0
  32. data/script/txt2html +82 -0
  33. data/spec/data/gpx/fells_loop.gpx +1077 -0
  34. data/spec/data/gpx/long.gpx +1642 -0
  35. data/spec/data/gpx/long.kml +31590 -0
  36. data/spec/data/gpx/long.nmea +2220 -0
  37. data/spec/data/gpx/short.gpx +13634 -0
  38. data/spec/data/gpx/short.kml +130 -0
  39. data/spec/data/gpx/tracktreks.gpx +706 -0
  40. data/spec/data/multipoint.dbf +0 -0
  41. data/spec/data/multipoint.shp +0 -0
  42. data/spec/data/multipoint.shx +0 -0
  43. data/spec/data/point.dbf +0 -0
  44. data/spec/data/point.shp +0 -0
  45. data/spec/data/point.shx +0 -0
  46. data/spec/data/polygon.dbf +0 -0
  47. data/spec/data/polygon.shp +0 -0
  48. data/spec/data/polygon.shx +0 -0
  49. data/spec/data/polyline.dbf +0 -0
  50. data/spec/data/polyline.shp +0 -0
  51. data/spec/data/polyline.shx +0 -0
  52. data/spec/geo_ruby/gpx4r/gpx_spec.rb +106 -0
  53. data/spec/geo_ruby/shp4r/shp_spec.rb +240 -0
  54. data/spec/geo_ruby/simple_features/envelope_spec.rb +45 -0
  55. data/spec/geo_ruby/simple_features/ewkb_parser_spec.rb +158 -0
  56. data/spec/geo_ruby/simple_features/ewkt_parser_spec.rb +179 -0
  57. data/spec/geo_ruby/simple_features/geometry_collection_spec.rb +55 -0
  58. data/spec/geo_ruby/simple_features/geometry_factory_spec.rb +11 -0
  59. data/spec/geo_ruby/simple_features/geometry_spec.rb +32 -0
  60. data/spec/geo_ruby/simple_features/georss_parser_spec.rb +218 -0
  61. data/spec/geo_ruby/simple_features/line_string_spec.rb +245 -0
  62. data/spec/geo_ruby/simple_features/linear_ring_spec.rb +14 -0
  63. data/spec/geo_ruby/simple_features/multi_line_string_spec.rb +54 -0
  64. data/spec/geo_ruby/simple_features/multi_point_spec.rb +35 -0
  65. data/spec/geo_ruby/simple_features/multi_polygon_spec.rb +50 -0
  66. data/spec/geo_ruby/simple_features/point_spec.rb +356 -0
  67. data/spec/geo_ruby/simple_features/polygon_spec.rb +108 -0
  68. data/spec/geo_ruby_spec.rb +27 -0
  69. data/spec/spec.opts +6 -0
  70. data/spec/spec_helper.rb +65 -0
  71. metadata +162 -0
@@ -0,0 +1,167 @@
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, :zoom
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
+ # def bounding_box(markers)
33
+ # max_lat, max_lon, min_lat, min_lon = -Float::MAX, -Float::MAX, Float::MAX, Float::MAX
34
+ # markers.each do |marker|
35
+ # coord = marker.point
36
+ # max_lat = coord.lat if coord.lat > max_lat
37
+ # min_lat = coord.lat if coord.lat < min_lat
38
+ # max_lon = coord.lng if coord.lng > max_lon
39
+ # min_lon = coord.lng if coord.lng < min_lon
40
+ # end
41
+ # min_point = Point.from_x_y(min_lat,min_lon)
42
+ # max_point = Point.from_x_y(max_lat,max_lon)
43
+
44
+ # end
45
+ # centrelat = (max_lat + min_lat)/2
46
+ # centrelng = (max_lon + min_lon)/2
47
+ # # logger.info("distance[#{distance}],zoom[#{zoom}]")
48
+ # #return GLatLngBounds.new(GLatLng.new(min_point),GLatLng.new(max_point)), [centrelat,centrelng], zoom
49
+ # return [centrelat,centrelng], zoom
50
+
51
+ #Sends back the center of the envelope
52
+ def center
53
+ Point.from_x_y((lower_corner.x + upper_corner.x)/2,(lower_corner.y + upper_corner.y)/2)
54
+ end
55
+
56
+ #Zoom level
57
+ def zoom
58
+ distance = lower_corner.spherical_distance(upper_corner)/10000
59
+ @zoom = case distance
60
+ when 150..9000 then 5
61
+ when 80..149 then 6
62
+ when 50..79 then 7
63
+ when 20..49 then 8
64
+ when 10..19 then 9
65
+ when 5..9 then 10
66
+ else 13
67
+ end
68
+ end
69
+
70
+ #Tests the equality of line strings
71
+ def ==(other_envelope)
72
+ if other_envelope.class != self.class
73
+ false
74
+ else
75
+ upper_corner == other_envelope.upper_corner and lower_corner == other_envelope.lower_corner
76
+ end
77
+ end
78
+
79
+ #georss serialization: Dialect can be passed as option <tt>:dialect</tt> and set to <tt>:simple</tt> (default)
80
+ #<tt>:w3cgeo</tt> or <tt>:gml</tt>. Options <tt>:featuretypetag
81
+ def as_georss(options = {})
82
+ dialect= options[:dialect] || :simple
83
+ case(dialect)
84
+ when :simple
85
+ geom_attr = ""
86
+ geom_attr += " featuretypetag=\"#{options[:featuretypetag]}\"" if options[:featuretypetag]
87
+ geom_attr += " relationshiptag=\"#{options[:relationshiptag]}\"" if options[:relationshiptag]
88
+ geom_attr += " floor=\"#{options[:floor]}\"" if options[:floor]
89
+ geom_attr += " radius=\"#{options[:radius]}\"" if options[:radius]
90
+ geom_attr += " elev=\"#{options[:elev]}\"" if options[:elev]
91
+
92
+ georss_simple_representation(options.merge(:geom_attr => geom_attr))
93
+ when :w3cgeo
94
+ georss_w3cgeo_representation(options)
95
+ when :gml
96
+ georss_gml_representation(options)
97
+ end
98
+ end
99
+
100
+ #georss simple representation
101
+ def georss_simple_representation(options = {}) #:nodoc:
102
+ georss_ns = options[:georss_ns] || "georss"
103
+ geom_attr = options[:geom_attr]
104
+ "<#{georss_ns}:box#{geom_attr}>#{lower_corner.y} #{lower_corner.x} #{upper_corner.y} #{upper_corner.x}</#{georss_ns}:box>\n"
105
+ end
106
+
107
+ #georss w3c representation : outputs the first point of the line
108
+ def georss_w3cgeo_representation(options = {}) #:nodoc:
109
+ w3cgeo_ns = options[:w3cgeo_ns] || "geo"
110
+ point = self.center
111
+ "<#{w3cgeo_ns}:lat>#{point.y}</#{w3cgeo_ns}:lat>\n<#{w3cgeo_ns}:long>#{point.x}</#{w3cgeo_ns}:long>\n"
112
+ end
113
+
114
+ #georss gml representation
115
+ def georss_gml_representation(options = {}) #:nodoc:
116
+ georss_ns = options[:georss_ns] || "georss"
117
+ gml_ns = options[:gml_ns] || "gml"
118
+ result = "<#{georss_ns}:where>\n<#{gml_ns}:Envelope>\n"
119
+ result += "<#{gml_ns}:LowerCorner>" + "#{lower_corner.y} #{lower_corner.x}" + "</#{gml_ns}:LowerCorner>"
120
+ result += "<#{gml_ns}:UpperCorner>" + "#{upper_corner.y} #{upper_corner.x}" + "</#{gml_ns}:UpperCorner>"
121
+ result += "</#{gml_ns}:Envelope>\n</#{georss_ns}:where>\n"
122
+ end
123
+
124
+ #Sends back a latlonaltbox
125
+ def as_kml(options = {})
126
+ geom_data = ""
127
+ geom_data = "<altitudeMode>#{options[:altitude_mode]}</altitudeMode>\n" if options[:altitude_mode]
128
+
129
+ allow_z = with_z && (!options[:altitude_mode].nil?) && options[:atitude_mode] != "clampToGround"
130
+
131
+ kml_representation(options.merge(:geom_data => geom_data,:allow_z => allow_z))
132
+ end
133
+
134
+ def kml_representation(options = {})#:nodoc:
135
+ result = "<LatLonAltBox>\n"
136
+ result += options[:geom_data]
137
+ result += "<north>#{upper_corner.y}</north>\n"
138
+ result += "<south>#{lower_corner.y}</south>\n"
139
+ result += "<east>#{upper_corner.x}</east>\n"
140
+ result += "<west>#{lower_corner.x}</west>\n"
141
+
142
+ if with_z
143
+ result += "<minAltitude>#{lower_corner.z}</minAltitude>"
144
+ result += "<maxAltitude>#{upper_corner.z}</maxAltitude>"
145
+ end
146
+
147
+ result += "</LatLonAltBox>\n"
148
+ end
149
+
150
+ #Creates a new envelope. Accept an array of 2 points as argument
151
+ def self.from_points(points,srid=DEFAULT_SRID,with_z=false)
152
+ raise "Not an array" unless points.class == Array
153
+ e = Envelope.new(srid,with_z)
154
+ e.lower_corner, e.upper_corner = points
155
+ e
156
+ end
157
+
158
+ #Creates a new envelope. Accept a sequence of point coordinates as argument : ((x,y),(x,y))
159
+ def self.from_coordinates(points,srid=DEFAULT_SRID,with_z=false)
160
+ e = Envelope.new(srid,with_z)
161
+ e.lower_corner, e.upper_corner = points.collect{|point_coords| Point.from_coordinates(point_coords,srid,with_z)}
162
+ e
163
+ end
164
+
165
+ end
166
+ end
167
+ 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