georuby_remake 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) 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 +47 -0
  6. data/VERSION +1 -0
  7. data/georuby_remake.gemspec +132 -0
  8. data/lib/geo_ruby.rb +5 -0
  9. data/lib/geo_ruby/gpx4r.rb +9 -0
  10. data/lib/geo_ruby/gpx4r/gpx.rb +104 -0
  11. data/lib/geo_ruby/shp4r.rb +57 -0
  12. data/lib/geo_ruby/shp4r/dbf.rb +38 -0
  13. data/lib/geo_ruby/shp4r/shp_file.rb +297 -0
  14. data/lib/geo_ruby/shp4r/shp_record.rb +14 -0
  15. data/lib/geo_ruby/shp4r/shp_transaction.rb +345 -0
  16. data/lib/geo_ruby/simple_features.rb +20 -0
  17. data/lib/geo_ruby/simple_features/envelope.rb +167 -0
  18. data/lib/geo_ruby/simple_features/ewkb_parser.rb +216 -0
  19. data/lib/geo_ruby/simple_features/ewkt_parser.rb +336 -0
  20. data/lib/geo_ruby/simple_features/geometry.rb +230 -0
  21. data/lib/geo_ruby/simple_features/geometry_collection.rb +136 -0
  22. data/lib/geo_ruby/simple_features/geometry_factory.rb +81 -0
  23. data/lib/geo_ruby/simple_features/georss_parser.rb +135 -0
  24. data/lib/geo_ruby/simple_features/helper.rb +18 -0
  25. data/lib/geo_ruby/simple_features/line_string.rb +206 -0
  26. data/lib/geo_ruby/simple_features/linear_ring.rb +12 -0
  27. data/lib/geo_ruby/simple_features/multi_line_string.rb +51 -0
  28. data/lib/geo_ruby/simple_features/multi_point.rb +46 -0
  29. data/lib/geo_ruby/simple_features/multi_polygon.rb +52 -0
  30. data/lib/geo_ruby/simple_features/point.rb +364 -0
  31. data/lib/geo_ruby/simple_features/polygon.rb +157 -0
  32. data/spec/data/gpx/fells_loop.gpx +1077 -0
  33. data/spec/data/gpx/long.gpx +1642 -0
  34. data/spec/data/gpx/long.kml +31590 -0
  35. data/spec/data/gpx/long.nmea +2220 -0
  36. data/spec/data/gpx/short.gpx +13634 -0
  37. data/spec/data/gpx/short.kml +130 -0
  38. data/spec/data/gpx/tracktreks.gpx +706 -0
  39. data/spec/data/multipoint.dbf +0 -0
  40. data/spec/data/multipoint.shp +0 -0
  41. data/spec/data/multipoint.shx +0 -0
  42. data/spec/data/point.dbf +0 -0
  43. data/spec/data/point.shp +0 -0
  44. data/spec/data/point.shx +0 -0
  45. data/spec/data/polygon.dbf +0 -0
  46. data/spec/data/polygon.shp +0 -0
  47. data/spec/data/polygon.shx +0 -0
  48. data/spec/data/polyline.dbf +0 -0
  49. data/spec/data/polyline.shp +0 -0
  50. data/spec/data/polyline.shx +0 -0
  51. data/spec/geo_ruby/gpx4r/gpx_spec.rb +106 -0
  52. data/spec/geo_ruby/shp4r/shp_spec.rb +240 -0
  53. data/spec/geo_ruby/simple_features/envelope_spec.rb +45 -0
  54. data/spec/geo_ruby/simple_features/ewkb_parser_spec.rb +158 -0
  55. data/spec/geo_ruby/simple_features/ewkt_parser_spec.rb +179 -0
  56. data/spec/geo_ruby/simple_features/geometry_collection_spec.rb +55 -0
  57. data/spec/geo_ruby/simple_features/geometry_factory_spec.rb +11 -0
  58. data/spec/geo_ruby/simple_features/geometry_spec.rb +32 -0
  59. data/spec/geo_ruby/simple_features/georss_parser_spec.rb +218 -0
  60. data/spec/geo_ruby/simple_features/line_string_spec.rb +245 -0
  61. data/spec/geo_ruby/simple_features/linear_ring_spec.rb +14 -0
  62. data/spec/geo_ruby/simple_features/multi_line_string_spec.rb +54 -0
  63. data/spec/geo_ruby/simple_features/multi_point_spec.rb +35 -0
  64. data/spec/geo_ruby/simple_features/multi_polygon_spec.rb +50 -0
  65. data/spec/geo_ruby/simple_features/point_spec.rb +356 -0
  66. data/spec/geo_ruby/simple_features/polygon_spec.rb +108 -0
  67. data/spec/geo_ruby_spec.rb +27 -0
  68. data/spec/spec.opts +6 -0
  69. data/spec/spec_helper.rb +65 -0
  70. metadata +185 -0
@@ -0,0 +1,230 @@
1
+ module GeoRuby#:nodoc:
2
+ module SimpleFeatures
3
+ #arbitrary default SRID
4
+ DEFAULT_SRID = 4326 unless defined? DEFAULT_SRID
5
+
6
+ #Root of all geometric data classes.
7
+ #Objects of class Geometry should not be instantiated.
8
+ class Geometry
9
+ #SRID of the geometry
10
+ attr_reader :srid #writer defined below
11
+ #Flag indicating if the z ordinate of the geometry is meaningful
12
+ attr_accessor :with_z
13
+ alias :with_z? :with_z
14
+ #Flag indicating if the m ordinate of the geometry is meaningful
15
+ attr_accessor :with_m
16
+ alias :with_m? :with_m
17
+
18
+ def initialize(srid=DEFAULT_SRID,with_z=false,with_m=false)
19
+ @srid=srid
20
+ @with_z=with_z
21
+ @with_m=with_m
22
+ end
23
+
24
+ def srid=(new_srid)
25
+ @srid = new_srid
26
+ unless self.is_a?(Point)
27
+ self.each do |geom|
28
+ geom.srid=new_srid
29
+ end
30
+ end
31
+ end
32
+
33
+ #to be implemented in subclasses
34
+ def bounding_box
35
+ end
36
+
37
+ #to be implemented in subclasses
38
+ def m_range
39
+ end
40
+
41
+ #Returns an Envelope object for the geometry
42
+ def envelope
43
+ Envelope.from_points(bounding_box,srid,with_z)
44
+ end
45
+
46
+ #Outputs the geometry as an EWKB string.
47
+ #The +allow_srid+, +allow_z+ and +allow_m+ arguments allow the output to include srid, z and m respectively if they are present in the geometry. If these arguments are set to false, srid, z and m are not included, even if they are present in the geometry. By default, the output string contains all the information in the object.
48
+ def as_ewkb(allow_srid=true,allow_z=true,allow_m=true)
49
+ ewkb="";
50
+
51
+ ewkb << 1.chr #little_endian by default
52
+
53
+ type= binary_geometry_type
54
+ if @with_z and allow_z
55
+ type = type | Z_MASK
56
+ end
57
+ if @with_m and allow_m
58
+ type = type | M_MASK
59
+ end
60
+ if allow_srid
61
+ type = type | SRID_MASK
62
+ ewkb << [type,@srid].pack("VV")
63
+ else
64
+ ewkb << [type].pack("V")
65
+ end
66
+
67
+ ewkb << binary_representation(allow_z,allow_m)
68
+ end
69
+
70
+ #Outputs the geometry as a strict WKB string.
71
+ def as_wkb
72
+ as_ewkb(false,false,false)
73
+ end
74
+
75
+ #Outputs the geometry as a HexEWKB string. It is almost the same as a WKB string, except that each byte of a WKB string is replaced by its hexadecimal 2-character representation in a HexEWKB string.
76
+ def as_hex_ewkb(allow_srid=true,allow_z=true,allow_m=true)
77
+ as_ewkb(allow_srid, allow_z, allow_m).unpack('H*').join('').upcase
78
+ end
79
+
80
+ #Outputs the geometry as a strict HexWKB string
81
+ def as_hex_wkb
82
+ as_hex_ewkb(false,false,false)
83
+ end
84
+
85
+ #Outputs the geometry as an EWKT string.
86
+ def as_ewkt(allow_srid=true,allow_z=true,allow_m=true)
87
+ if allow_srid
88
+ ewkt="SRID=#{@srid};"
89
+ else
90
+ ewkt=""
91
+ end
92
+ ewkt << text_geometry_type
93
+ ewkt << "M" if @with_m and allow_m and (!@with_z or !allow_z) #to distinguish the M from the Z when there is actually no Z...
94
+ ewkt << "(" << text_representation(allow_z,allow_m) << ")"
95
+ end
96
+
97
+ #Outputs the geometry as strict WKT string.
98
+ def as_wkt
99
+ as_ewkt(false,false,false)
100
+ end
101
+
102
+ #Outputs the geometry in georss format.
103
+ #Assumes the geometries are in latlon format, with x as lon and y as lat.
104
+ #Pass the <tt>:dialect</tt> option to switch format. Possible values are: <tt>:simple</tt> (default), <tt>:w3cgeo</tt> and <tt>:gml</tt>.
105
+ def as_georss(options = {})
106
+ dialect= options[:dialect] || :simple
107
+ case(dialect)
108
+ when :simple
109
+ geom_attr = ""
110
+ geom_attr += " featuretypetag=\"#{options[:featuretypetag]}\"" if options[:featuretypetag]
111
+ geom_attr += " relationshiptag=\"#{options[:relationshiptag]}\"" if options[:relationshiptag]
112
+ geom_attr += " floor=\"#{options[:floor]}\"" if options[:floor]
113
+ geom_attr += " radius=\"#{options[:radius]}\"" if options[:radius]
114
+ geom_attr += " elev=\"#{options[:elev]}\"" if options[:elev]
115
+ georss_simple_representation(options.merge(:geom_attr => geom_attr))
116
+ when :w3cgeo
117
+ georss_w3cgeo_representation(options)
118
+ when :gml
119
+ georss_gml_representation(options)
120
+ end
121
+ end
122
+
123
+ #Iutputs the geometry in kml format : options are <tt>:id</tt>, <tt>:tesselate</tt>, <tt>:extrude</tt>,
124
+ #<tt>:altitude_mode</tt>. If the altitude_mode option is not present, the Z (if present) will not be output (since
125
+ #it won't be used by GE anyway: clampToGround is the default)
126
+ def as_kml(options = {})
127
+ id_attr = ""
128
+ id_attr = " id=\"#{options[:id]}\"" if options[:id]
129
+
130
+ geom_data = ""
131
+ geom_data += "<extrude>#{options[:extrude]}</extrude>\n" if options[:extrude]
132
+ geom_data += "<tesselate>#{options[:tesselate]}</tesselate>\n" if options[:tesselate]
133
+ geom_data += "<altitudeMode>#{options[:altitude_mode]}</altitudeMode>\n" if options[:altitude_mode]
134
+
135
+ allow_z = (with_z || !options[:altitude].nil? )&& (!options[:altitude_mode].nil?) && options[:atitude_mode] != "clampToGround"
136
+ fixed_z = options[:altitude]
137
+
138
+ kml_representation(options.merge(:id_attr => id_attr, :geom_data => geom_data, :allow_z => allow_z, :fixed_z => fixed_z))
139
+ end
140
+
141
+ #Creates a geometry based on a EWKB string. The actual class returned depends of the content of the string passed as argument. Since WKB strings are a subset of EWKB, they are also valid.
142
+ def self.from_ewkb(ewkb)
143
+ factory = GeometryFactory::new
144
+ ewkb_parser= EWKBParser::new(factory)
145
+ ewkb_parser.parse(ewkb)
146
+ factory.geometry
147
+ end
148
+
149
+ #Creates a geometry based on a HexEWKB string
150
+ def self.from_hex_ewkb(hexewkb)
151
+ factory = GeometryFactory::new
152
+ hexewkb_parser= HexEWKBParser::new(factory)
153
+ hexewkb_parser.parse(hexewkb)
154
+ factory.geometry
155
+ end
156
+
157
+ #Creates a geometry based on a EWKT string. Since WKT strings are a subset of EWKT, they are also valid.
158
+ def self.from_ewkt(ewkt)
159
+ factory = GeometryFactory::new
160
+ ewkt_parser= EWKTParser::new(factory)
161
+ ewkt_parser.parse(ewkt)
162
+ factory.geometry
163
+ end
164
+
165
+ #sends back a geometry based on the GeoRSS string passed as argument
166
+ def self.from_georss(georss)
167
+ georss_parser= GeorssParser::new
168
+ georss_parser.parse(georss)
169
+ georss_parser.geometry
170
+ end
171
+
172
+ #sends back an array: The first element is the goemetry based on the GeoRSS string passed as argument. The second one is the GeoRSSTags (found only with the Simple format)
173
+ def self.from_georss_with_tags(georss)
174
+ georss_parser= GeorssParser::new
175
+ georss_parser.parse(georss,true)
176
+ [georss_parser.geometry, georss_parser.georss_tags]
177
+ end
178
+
179
+ #Sends back a geometry from a KML encoded geometry string.
180
+ #Limitations : Only supports points, linestrings and polygons (no collection for now).
181
+ #Addapted from Pramukta's code
182
+ def self.from_kml(kml)
183
+ return GeoRuby::SimpleFeatures::Geometry.from_ewkt(kml_to_wkt(kml))
184
+ end
185
+
186
+ require 'rexml/document'
187
+ def self.kml_to_wkt(kml)
188
+ doc = REXML::Document.new(kml)
189
+ wkt = ""
190
+ if ["Point", "LineString", "Polygon" ].include?(doc.root.name)
191
+ case doc.root.name
192
+ when "Point" then
193
+ coords = doc.elements["/Point/coordinates"].text.gsub(/\n/," ")
194
+ wkt = doc.root.name.upcase + "(" + split_coords(coords).join(' ') + ")"
195
+ when "LineString" then
196
+ coords = doc.elements["/LineString/coordinates"].text.gsub(/\n/," ")
197
+ coords = split_coords(coords)
198
+ wkt = doc.root.name.upcase + "(" + coords.join(",") + ")"
199
+ when "Polygon" then
200
+ # polygons have one outer ring and zero or more inner rings
201
+ bounds = []
202
+ bounds << doc.elements["/Polygon/outerBoundaryIs/LinearRing/coordinates"].text
203
+ inner_coords_elements = doc.elements.each("/Polygon/innerBoundaryIs/LinearRing/coordinates") do |inner_coords|
204
+ inner_coords = inner_coords.text
205
+ bounds << inner_coords
206
+ end
207
+
208
+ wkt = doc.root.name.upcase + "(" + bounds.map do |bound|
209
+ bound.gsub!(/\n/, " ")
210
+ bound = split_coords(bound)
211
+ if bound.first != bound.last
212
+ bound.push bound.first
213
+ end
214
+ "(" + bound.join(",") + ")"
215
+ end.join(",") + ")"
216
+ end
217
+ end
218
+ return wkt
219
+ end
220
+
221
+ private
222
+
223
+ def self.split_coords(coords)
224
+ coords.split(" ").collect { |coord|
225
+ coord.gsub(","," ")
226
+ }
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,136 @@
1
+ #require 'geo_ruby/simple_features/geometry'
2
+
3
+ module GeoRuby
4
+ module SimpleFeatures
5
+ #Represents a collection of arbitrary geometries
6
+ class GeometryCollection < Geometry
7
+ attr_reader :geometries
8
+
9
+ def initialize(srid = DEFAULT_SRID,with_z=false,with_m=false)
10
+ super(srid,with_z,with_m)
11
+ @geometries = []
12
+ end
13
+
14
+ #Delegate the unknown methods to the geometries array
15
+ def method_missing(method_name,*args,&b)
16
+ @geometries.send(method_name,*args,&b)
17
+ end
18
+
19
+ #Bounding box in 2D/3D. Returns an array of 2 points
20
+ def bounding_box
21
+ max_x, min_x, max_y, min_y = -Float::MAX, Float::MAX, -Float::MAX, Float::MAX, -Float::MAX, Float::MAX
22
+ if with_z
23
+ max_z, min_z = -Float::MAX, Float::MAX
24
+ each do |geometry|
25
+ bbox = geometry.bounding_box
26
+ sw = bbox[0]
27
+ ne = bbox[1]
28
+
29
+ max_y = ne.y if ne.y > max_y
30
+ min_y = sw.y if sw.y < min_y
31
+ max_x = ne.x if ne.x > max_x
32
+ min_x = sw.x if sw.x < min_x
33
+ max_z = ne.z if ne.z > max_z
34
+ min_z = sw.z if sw.z < min_z
35
+ end
36
+ [Point.from_x_y_z(min_x,min_y,min_z),Point.from_x_y_z(max_x,max_y,max_z)]
37
+ else
38
+ each do |geometry|
39
+ bbox = geometry.bounding_box
40
+ sw = bbox[0]
41
+ ne = bbox[1]
42
+
43
+ max_y = ne.y if ne.y > max_y
44
+ min_y = sw.y if sw.y < min_y
45
+ max_x = ne.x if ne.x > max_x
46
+ min_x = sw.x if sw.x < min_x
47
+ end
48
+ [Point.from_x_y(min_x,min_y),Point.from_x_y(max_x,max_y)]
49
+ end
50
+ end
51
+
52
+ def m_range
53
+ if with_m
54
+ max_m, min_m = -Float::MAX, Float::MAX
55
+ each do |lr|
56
+ lrmr = lr.m_range
57
+ max_m = lrmr[1] if lrmr[1] > max_m
58
+ min_m = lrmr[0] if lrmr[0] < min_m
59
+ end
60
+ [min_m,max_m]
61
+ else
62
+ [0,0]
63
+ end
64
+ end
65
+
66
+ #tests the equality of geometry collections
67
+ def ==(other_collection)
68
+ if(other_collection.class != self.class)
69
+ false
70
+ elsif length != other_collection.length
71
+ false
72
+ else
73
+ index=0
74
+ while index<length
75
+ return false if self[index] != other_collection[index]
76
+ index+=1
77
+ end
78
+ true
79
+ end
80
+ end
81
+
82
+ #Binary representation of the collection
83
+ def binary_representation(allow_z=true,allow_m=true) #:nodoc:
84
+ rep = [length].pack("V")
85
+ #output the list of geometries without outputting the SRID first and with the same setting regarding Z and M
86
+ each {|geometry| rep << geometry.as_ewkb(false,allow_z,allow_m) }
87
+ rep
88
+ end
89
+
90
+ #WKB geometry type of the collection
91
+ def binary_geometry_type #:nodoc:
92
+ 7
93
+ end
94
+
95
+ #Text representation of a geometry collection
96
+ def text_representation(allow_z=true,allow_m=true) #:nodoc:
97
+ @geometries.collect{|geometry| geometry.as_ewkt(false,allow_z,allow_m)}.join(",")
98
+ end
99
+
100
+ #WKT geometry type
101
+ def text_geometry_type #:nodoc:
102
+ "GEOMETRYCOLLECTION"
103
+ end
104
+
105
+ #georss simple representation : outputs only the first geometry of the collection
106
+ def georss_simple_representation(options)#:nodoc:
107
+ self[0].georss_simple_representation(options)
108
+ end
109
+ #georss w3c representation : outputs the first point of the outer ring
110
+ def georss_w3cgeo_representation(options)#:nodoc:
111
+ self[0].georss_w3cgeo_representation(options)
112
+ end
113
+ #georss gml representation : outputs only the first geometry of the collection
114
+ def georss_gml_representation(options)#:nodoc:
115
+ self[0].georss_gml_representation(options)
116
+ end
117
+
118
+ #outputs the geometry in kml format
119
+ def kml_representation(options = {}) #:nodoc:
120
+ result = "<MultiGeometry#{options[:id_attr]}>\n"
121
+ options[:id_attr] = "" #the subgeometries do not have an ID
122
+ each do |geometry|
123
+ result += geometry.kml_representation(options)
124
+ end
125
+ result += "</MultiGeometry>\n"
126
+ end
127
+
128
+ #creates a new GeometryCollection from an array of geometries
129
+ def self.from_geometries(geometries,srid=DEFAULT_SRID,with_z=false,with_m=false)
130
+ geometry_collection = new(srid,with_z,with_m)
131
+ geometry_collection.concat(geometries)
132
+ geometry_collection
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,81 @@
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
+
11
+ module GeoRuby
12
+ module SimpleFeatures
13
+ #Creates a new geometry according to constructions received from a parser, for example EWKBParser.
14
+ class GeometryFactory
15
+ #the built geometry
16
+ attr_reader :geometry
17
+
18
+ def initialize
19
+ @geometry = nil
20
+ @geometry_stack = []
21
+ end
22
+ #resets the factory
23
+ def reset
24
+ @geometry = nil
25
+ @geometry_stack = []
26
+ end
27
+ #add a 2D point to the current geometry
28
+ def add_point_x_y(x,y)
29
+ @geometry_stack.last.set_x_y(x,y)
30
+ end
31
+ #add 2D points to the current geometry
32
+ def add_points_x_y(xy)
33
+ xy.each_slice(2) {|slice| add_point_x_y(*slice)}
34
+ end
35
+ #add a 3D point to the current geometry
36
+ def add_point_x_y_z(x,y,z)
37
+ @geometry_stack.last.set_x_y_z(x,y,z)
38
+ end
39
+ #add 3D points to the current geometry
40
+ def add_points_x_y_z(xyz)
41
+ xyz.each_slice(3) {|slice| add_point_x_y_z(*slice)}
42
+ end
43
+ #add a 2D point with M to the current geometry
44
+ def add_point_x_y_m(x,y,m)
45
+ @geometry_stack.last.set_x_y(x,y)
46
+ @geometry_stack.last.m=m
47
+ end
48
+ #add 2D points with M to the current geometry
49
+ def add_points_x_y_m(xym)
50
+ xym.each_slice(3) {|slice| add_point_x_y_m(*slice)}
51
+ end
52
+ #add a 3D point with M to the current geometry
53
+ def add_point_x_y_z_m(x,y,z,m)
54
+ @geometry_stack.last.set_x_y_z(x,y,z)
55
+ @geometry_stack.last.m=m
56
+ end
57
+ #add 3D points with M to the current geometry
58
+ def add_points_x_y_z_m(xyzm)
59
+ xyzm.each_slice(4) {|slice| add_point_x_y_z_m(*slice)}
60
+ end
61
+ #begin a geometry of type +geometry_type+
62
+ def begin_geometry(geometry_type,srid=DEFAULT_SRID)
63
+ geometry= geometry_type::new(srid)
64
+ @geometry= geometry if @geometry.nil?
65
+ @geometry_stack << geometry
66
+ end
67
+ #terminates the current geometry
68
+ def end_geometry(with_z=false,with_m=false)
69
+ @geometry=@geometry_stack.pop
70
+ @geometry.with_z=with_z
71
+ @geometry.with_m=with_m
72
+ #add the newly defined geometry to its parent if there is one
73
+ @geometry_stack.last << geometry if !@geometry_stack.empty?
74
+ end
75
+ #abort a geometry
76
+ def abort_geometry
77
+ reset
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,135 @@
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
+ #require 'geo_ruby/simple_features/envelope'
10
+
11
+ module GeoRuby
12
+ module SimpleFeatures
13
+
14
+ #Raised when an error in the GeoRSS string is detected
15
+ class GeorssFormatError < StandardError
16
+ end
17
+
18
+ #Contains tags possibly found on GeoRss Simple geometries
19
+ class GeorssTags < Struct.new(:featuretypetag,:relationshiptag,:elev,:floor,:radius)
20
+ end
21
+
22
+ #Parses GeoRSS strings
23
+ #You can also use directly the static method Geometry.from_georss
24
+ class GeorssParser
25
+ attr_reader :georss_tags, :geometry
26
+
27
+ #Parses the georss geometry passed as argument and notifies the factory of events
28
+ #The parser assumes
29
+ def parse(georss,with_tags = false)
30
+ @geometry = nil
31
+ @georss_tags = GeorssTags.new
32
+ parse_geometry(georss,with_tags)
33
+ end
34
+
35
+ private
36
+ def parse_geometry(georss,with_tags)
37
+ georss.strip!
38
+ #check for W3CGeo first
39
+ if georss =~ /<[^:>]*:lat\s*>([^<]*)</
40
+ #if valid, it is W3CGeo
41
+ lat = $1.to_f
42
+ if georss =~ /<[^:>]*:long\s*>([^<]*)</
43
+ lon = $1.to_f
44
+ @geometry = Point.from_x_y(lon,lat)
45
+ else
46
+ raise GeorssFormatError.new("Bad W3CGeo GeoRSS format")
47
+ end
48
+ elsif georss =~ /^<\s*[^:>]*:where\s*>/
49
+ #GML format found
50
+ gml = $'.strip
51
+ if gml =~ /^<\s*[^:>]*:Point\s*>/
52
+ #gml point
53
+ if gml =~ /<\s*[^:>]*:pos\s*>([^<]*)/
54
+ point = $1.split(" ")
55
+ #lat comes first
56
+ @geometry = Point.from_x_y(point[1].to_f,point[0].to_f)
57
+ else
58
+ raise GeorssFormatError.new("Bad GML GeoRSS format: Malformed Point")
59
+ end
60
+ elsif gml =~ /^<\s*[^:>]*:LineString\s*>/
61
+ if gml =~ /<\s*[^:>]*:posList\s*>([^<]*)/
62
+ xy = $1.split(" ")
63
+ @geometry = LineString.new
64
+ 0.upto(xy.size/2 - 1) { |index| @geometry << Point.from_x_y(xy[index*2 + 1].to_f,xy[index*2].to_f)}
65
+ else
66
+ raise GeorssFormatError.new("Bad GML GeoRSS format: Malformed LineString")
67
+ end
68
+ elsif gml =~ /^<\s*[^:>]*:Polygon\s*>/
69
+ if gml =~ /<\s*[^:>]*:posList\s*>([^<]*)/
70
+ xy = $1.split(" ")
71
+ @geometry = Polygon.new
72
+ linear_ring = LinearRing.new
73
+ @geometry << linear_ring
74
+ xy = $1.split(" ")
75
+ 0.upto(xy.size/2 - 1) { |index| linear_ring << Point.from_x_y(xy[index*2 + 1].to_f,xy[index*2].to_f)}
76
+ else
77
+ raise GeorssFormatError.new("Bad GML GeoRSS format: Malformed Polygon")
78
+ end
79
+ elsif gml =~ /^<\s*[^:>]*:Envelope\s*>/
80
+ if gml =~ /<\s*[^:>]*:lowerCorner\s*>([^<]*)</
81
+ lc = $1.split(" ").collect { |x| x.to_f}.reverse
82
+ if gml =~ /<\s*[^:>]*:upperCorner\s*>([^<]*)</
83
+ uc = $1.split(" ").collect { |x| x.to_f}.reverse
84
+ @geometry = Envelope.from_coordinates([lc,uc])
85
+ else
86
+ raise GeorssFormatError.new("Bad GML GeoRSS format: Malformed Envelope")
87
+ end
88
+ else
89
+ raise GeorssFormatError.new("Bad GML GeoRSS format: Malformed Envelope")
90
+ end
91
+ else
92
+ raise GeorssFormatError.new("Bad GML GeoRSS format: Unknown geometry type")
93
+ end
94
+ else
95
+ #must be simple format
96
+ if georss =~ /^<\s*[^>:]*:point([^>]*)>(.*)</m
97
+ tags = $1
98
+ point = $2.gsub(","," ").split(" ")
99
+ @geometry = Point.from_x_y(point[1].to_f,point[0].to_f)
100
+ elsif georss =~ /^<\s*[^>:]*:line([^>]*)>(.*)</m
101
+ tags = $1
102
+ @geometry = LineString.new
103
+ xy = $2.gsub(","," ").split(" ")
104
+ 0.upto(xy.size/2 - 1) { |index| @geometry << Point.from_x_y(xy[index*2 + 1].to_f,xy[index*2].to_f)}
105
+ elsif georss =~ /^<\s*[^>:]*:polygon([^>]*)>(.*)</m
106
+ tags = $1
107
+ @geometry = Polygon.new
108
+ linear_ring = LinearRing.new
109
+ @geometry << linear_ring
110
+ xy = $2.gsub(","," ").split(" ")
111
+ 0.upto(xy.size/2 - 1) { |index| linear_ring << Point.from_x_y(xy[index*2 + 1].to_f,xy[index*2].to_f)}
112
+ elsif georss =~ /^<\s*[^>:]*:box([^>]*)>(.*)</m
113
+ tags = $1
114
+ corners = []
115
+ xy = $2.gsub(","," ").split(" ")
116
+ 0.upto(xy.size/2 - 1) {|index| corners << Point.from_x_y(xy[index*2 + 1].to_f,xy[index*2].to_f)}
117
+ @geometry = Envelope.from_points(corners)
118
+ else
119
+ raise GeorssFormatError.new("Bad Simple GeoRSS format: Unknown geometry type")
120
+ end
121
+
122
+ #geometry found: parse tags
123
+ return unless with_tags
124
+
125
+ @georss_tags.featuretypetag = $1 if tags =~ /featuretypetag=['"]([^"']*)['"]/
126
+ @georss_tags.relationshiptag = $1 if tags =~ /relationshiptag=['"]([^'"]*)['"]/
127
+ @georss_tags.elev = $1.to_f if tags =~ /elev=['"]([^'"]*)['"]/
128
+ @georss_tags.floor = $1.to_i if tags =~ /floor=['"]([^'"]*)['"]/
129
+ @georss_tags.radius = $1.to_f if tags =~ /radius=['"]([^'"]*)['"]/
130
+
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end