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,225 @@
|
|
1
|
+
module GeoRuby#:nodoc:
|
2
|
+
module SimpleFeatures
|
3
|
+
#arbitrary default SRID
|
4
|
+
DEFAULT_SRID=-1
|
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
|
+
#Flag indicating if the m ordinate of the geometry is meaningful
|
14
|
+
attr_accessor :with_m
|
15
|
+
|
16
|
+
def initialize(srid=DEFAULT_SRID,with_z=false,with_m=false)
|
17
|
+
@srid=srid
|
18
|
+
@with_z=with_z
|
19
|
+
@with_m=with_m
|
20
|
+
end
|
21
|
+
|
22
|
+
def srid=(new_srid)
|
23
|
+
@srid = new_srid
|
24
|
+
unless self.is_a?(Point)
|
25
|
+
self.each do |geom|
|
26
|
+
geom.srid=new_srid
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
#to be implemented in subclasses
|
33
|
+
def bounding_box
|
34
|
+
end
|
35
|
+
|
36
|
+
#to be implemented in subclasses
|
37
|
+
def m_range
|
38
|
+
end
|
39
|
+
|
40
|
+
#Returns an Envelope object for the geometry
|
41
|
+
def envelope
|
42
|
+
Envelope.from_points(bounding_box,srid,with_z)
|
43
|
+
end
|
44
|
+
|
45
|
+
#Outputs the geometry as an EWKB string.
|
46
|
+
#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.
|
47
|
+
def as_ewkb(allow_srid=true,allow_z=true,allow_m=true)
|
48
|
+
ewkb="";
|
49
|
+
|
50
|
+
ewkb << 1.chr #little_endian by default
|
51
|
+
|
52
|
+
type= binary_geometry_type
|
53
|
+
if @with_z and allow_z
|
54
|
+
type = type | Z_MASK
|
55
|
+
end
|
56
|
+
if @with_m and allow_m
|
57
|
+
type = type | M_MASK
|
58
|
+
end
|
59
|
+
if allow_srid
|
60
|
+
type = type | SRID_MASK
|
61
|
+
ewkb << [type,@srid].pack("VV")
|
62
|
+
else
|
63
|
+
ewkb << [type].pack("V")
|
64
|
+
end
|
65
|
+
|
66
|
+
ewkb << binary_representation(allow_z,allow_m)
|
67
|
+
end
|
68
|
+
|
69
|
+
#Outputs the geometry as a strict WKB string.
|
70
|
+
def as_wkb
|
71
|
+
as_ewkb(false,false,false)
|
72
|
+
end
|
73
|
+
|
74
|
+
#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.
|
75
|
+
def as_hex_ewkb(allow_srid=true,allow_z=true,allow_m=true)
|
76
|
+
as_ewkb(allow_srid, allow_z, allow_m).unpack('H*').join('').upcase
|
77
|
+
end
|
78
|
+
#Outputs the geometry as a strict HexWKB string
|
79
|
+
def as_hex_wkb
|
80
|
+
as_hex_ewkb(false,false,false)
|
81
|
+
end
|
82
|
+
|
83
|
+
#Outputs the geometry as an EWKT string.
|
84
|
+
def as_ewkt(allow_srid=true,allow_z=true,allow_m=true)
|
85
|
+
if allow_srid
|
86
|
+
ewkt="SRID=#{@srid};"
|
87
|
+
else
|
88
|
+
ewkt=""
|
89
|
+
end
|
90
|
+
ewkt << text_geometry_type
|
91
|
+
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...
|
92
|
+
ewkt << "(" << text_representation(allow_z,allow_m) << ")"
|
93
|
+
end
|
94
|
+
|
95
|
+
#Outputs the geometry as strict WKT string.
|
96
|
+
def as_wkt
|
97
|
+
as_ewkt(false,false,false)
|
98
|
+
end
|
99
|
+
|
100
|
+
#Outputs the geometry in georss format.
|
101
|
+
#Assumes the geometries are in latlon format, with x as lon and y as lat.
|
102
|
+
#Pass the <tt>:dialect</tt> option to swhit format. Possible values are: <tt>:simple</tt> (default), <tt>:w3cgeo</tt> and <tt>:gml</tt>.
|
103
|
+
def as_georss(options = {})
|
104
|
+
dialect= options[:dialect] || :simple
|
105
|
+
case(dialect)
|
106
|
+
when :simple
|
107
|
+
geom_attr = ""
|
108
|
+
geom_attr += " featuretypetag=\"#{options[:featuretypetag]}\"" if options[:featuretypetag]
|
109
|
+
geom_attr += " relationshiptag=\"#{options[:relationshiptag]}\"" if options[:relationshiptag]
|
110
|
+
geom_attr += " floor=\"#{options[:floor]}\"" if options[:floor]
|
111
|
+
geom_attr += " radius=\"#{options[:radius]}\"" if options[:radius]
|
112
|
+
geom_attr += " elev=\"#{options[:elev]}\"" if options[:elev]
|
113
|
+
georss_simple_representation(options.merge(:geom_attr => geom_attr))
|
114
|
+
when :w3cgeo
|
115
|
+
georss_w3cgeo_representation(options)
|
116
|
+
when :gml
|
117
|
+
georss_gml_representation(options)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
#Iutputs the geometry in kml format : options are <tt>:id</tt>, <tt>:tesselate</tt>, <tt>:extrude</tt>,
|
122
|
+
#<tt>:altitude_mode</tt>. If the altitude_mode option is not present, the Z (if present) will not be output (since
|
123
|
+
#it won't be used by GE anyway: clampToGround is the default)
|
124
|
+
def as_kml(options = {})
|
125
|
+
id_attr = ""
|
126
|
+
id_attr = " id=\"#{options[:id]}\"" if options[:id]
|
127
|
+
|
128
|
+
geom_data = ""
|
129
|
+
geom_data += "<extrude>#{options[:extrude]}</extrude>\n" if options[:extrude]
|
130
|
+
geom_data += "<tesselate>#{options[:tesselate]}</tesselate>\n" if options[:tesselate]
|
131
|
+
geom_data += "<altitudeMode>#{options[:altitude_mode]}</altitudeMode>\n" if options[:altitude_mode]
|
132
|
+
|
133
|
+
allow_z = (with_z || !options[:altitude].nil? )&& (!options[:altitude_mode].nil?) && options[:atitude_mode] != "clampToGround"
|
134
|
+
fixed_z = options[:altitude]
|
135
|
+
|
136
|
+
kml_representation(options.merge(:id_attr => id_attr, :geom_data => geom_data, :allow_z => allow_z, :fixed_z => fixed_z))
|
137
|
+
end
|
138
|
+
|
139
|
+
#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.
|
140
|
+
def self.from_ewkb(ewkb)
|
141
|
+
factory = GeometryFactory::new
|
142
|
+
ewkb_parser= EWKBParser::new(factory)
|
143
|
+
ewkb_parser.parse(ewkb)
|
144
|
+
factory.geometry
|
145
|
+
end
|
146
|
+
#Creates a geometry based on a HexEWKB string
|
147
|
+
def self.from_hex_ewkb(hexewkb)
|
148
|
+
factory = GeometryFactory::new
|
149
|
+
hexewkb_parser= HexEWKBParser::new(factory)
|
150
|
+
hexewkb_parser.parse(hexewkb)
|
151
|
+
factory.geometry
|
152
|
+
end
|
153
|
+
#Creates a geometry based on a EWKT string. Since WKT strings are a subset of EWKT, they are also valid.
|
154
|
+
def self.from_ewkt(ewkt)
|
155
|
+
factory = GeometryFactory::new
|
156
|
+
ewkt_parser= EWKTParser::new(factory)
|
157
|
+
ewkt_parser.parse(ewkt)
|
158
|
+
factory.geometry
|
159
|
+
end
|
160
|
+
|
161
|
+
#sends back a geometry based on the GeoRSS string passed as argument
|
162
|
+
def self.from_georss(georss)
|
163
|
+
georss_parser= GeorssParser::new
|
164
|
+
georss_parser.parse(georss)
|
165
|
+
georss_parser.geometry
|
166
|
+
end
|
167
|
+
#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)
|
168
|
+
def self.from_georss_with_tags(georss)
|
169
|
+
georss_parser= GeorssParser::new
|
170
|
+
georss_parser.parse(georss,true)
|
171
|
+
[georss_parser.geometry, georss_parser.georss_tags]
|
172
|
+
end
|
173
|
+
|
174
|
+
#Sends back a geometry from a KML encoded geometry string.
|
175
|
+
#Limitations : Only supports points, linestrings and polygons (no collection for now).
|
176
|
+
#Addapted from Pramukta's code
|
177
|
+
def self.from_kml(kml)
|
178
|
+
return GeoRuby::SimpleFeatures::Geometry.from_ewkt(kml_to_wkt(kml))
|
179
|
+
end
|
180
|
+
|
181
|
+
require 'rexml/document'
|
182
|
+
def self.kml_to_wkt(kml)
|
183
|
+
doc = REXML::Document.new(kml)
|
184
|
+
wkt = ""
|
185
|
+
if ["Point", "LineString", "Polygon" ].include?(doc.root.name)
|
186
|
+
case doc.root.name
|
187
|
+
when "Point" then
|
188
|
+
coords = doc.elements["/Point/coordinates"].text.gsub(/\n/," ")
|
189
|
+
wkt = doc.root.name.upcase + "(" + split_coords(coords).join(' ') + ")"
|
190
|
+
when "LineString" then
|
191
|
+
coords = doc.elements["/LineString/coordinates"].text.gsub(/\n/," ")
|
192
|
+
coords = split_coords(coords)
|
193
|
+
wkt = doc.root.name.upcase + "(" + coords.join(",") + ")"
|
194
|
+
when "Polygon" then
|
195
|
+
# polygons have one outer ring and zero or more inner rings
|
196
|
+
bounds = []
|
197
|
+
bounds << doc.elements["/Polygon/outerBoundaryIs/LinearRing/coordinates"].text
|
198
|
+
inner_coords_elements = doc.elements.each("/Polygon/innerBoundaryIs/LinearRing/coordinates") do |inner_coords|
|
199
|
+
inner_coords = inner_coords.text
|
200
|
+
bounds << inner_coords
|
201
|
+
end
|
202
|
+
|
203
|
+
wkt = doc.root.name.upcase + "(" + bounds.map do |bound|
|
204
|
+
bound.gsub!(/\n/, " ")
|
205
|
+
bound = split_coords(bound)
|
206
|
+
if bound.first != bound.last
|
207
|
+
bound.push bound.first
|
208
|
+
end
|
209
|
+
"(" + bound.join(",") + ")"
|
210
|
+
end.join(",") + ")"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
return wkt
|
214
|
+
end
|
215
|
+
|
216
|
+
private
|
217
|
+
|
218
|
+
def self.split_coords(coords)
|
219
|
+
coords.split(" ").collect { |coord|
|
220
|
+
coord.gsub(","," ")
|
221
|
+
}
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
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
|