nofxx-georuby 1.9.0 → 1.9.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source :rubygems
2
+
3
+ group :development do
4
+ gem "dbf", ">= 1.5.0"
5
+ gem "rspec", ">= 2.3.0"
6
+ gem "bundler", ">= 1.0.0"
7
+ gem "rcov", ">= 0"
8
+ end
@@ -0,0 +1,29 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activesupport (3.0.5)
5
+ dbf (1.5.2)
6
+ activesupport (~> 3.0.0)
7
+ fastercsv (= 1.5.3)
8
+ i18n (~> 0.4.2)
9
+ diff-lcs (1.1.2)
10
+ fastercsv (1.5.3)
11
+ i18n (0.4.2)
12
+ rcov (0.9.9)
13
+ rspec (2.5.0)
14
+ rspec-core (~> 2.5.0)
15
+ rspec-expectations (~> 2.5.0)
16
+ rspec-mocks (~> 2.5.0)
17
+ rspec-core (2.5.1)
18
+ rspec-expectations (2.5.0)
19
+ diff-lcs (~> 1.1.2)
20
+ rspec-mocks (2.5.0)
21
+
22
+ PLATFORMS
23
+ ruby
24
+
25
+ DEPENDENCIES
26
+ bundler (>= 1.0.0)
27
+ dbf (>= 1.5.0)
28
+ rcov
29
+ rspec (>= 2.3.0)
data/Rakefile CHANGED
@@ -10,7 +10,6 @@ begin
10
10
  gem.email = "georuby@simplitex.com"
11
11
  gem.homepage = "http://github.com/nofxx/georuby"
12
12
  gem.authors = ["Guilhem Vellut", "Marcos Piccinini", "Marcus Mateus", "Doug Cole"]
13
- gem.add_runtime_dependency "json_pure", ">=1.4.6"
14
13
  gem.add_development_dependency "rspec", ">= 2.0.0"
15
14
  gem.add_development_dependency "dbf", ">= 1.2.9"
16
15
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.9.0
1
+ 1.9.1
@@ -1,5 +1,5 @@
1
- $:.unshift(File.dirname(__FILE__)) unless
2
- $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
1
+ # $:.unshift(File.dirname(__FILE__)) #unless
2
+ # $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
3
 
4
4
  require 'geo_ruby/simple_features/helper'
5
5
  require 'geo_ruby/simple_features/ewkt_parser'
@@ -15,9 +15,9 @@ require 'geo_ruby/simple_features/multi_polygon'
15
15
  require 'geo_ruby/simple_features/geometry_collection'
16
16
  require 'geo_ruby/simple_features/envelope'
17
17
  require 'geo_ruby/simple_features/geometry_factory'
18
- require 'geo_ruby/simple_features/georss_parser'
19
- require 'geo_ruby/simple_features/geojson_parser'
20
18
 
21
- # Include if you need
19
+ # Require if you need
22
20
  # require 'geo_ruby/shp4r/shp'
23
21
  # require 'geo_ruby/gpx4r/gpx'
22
+ # require 'geo_ruby/geojson'
23
+ # require 'geo_ruby/georss'
@@ -0,0 +1,129 @@
1
+ # GeoJSON parser based on the v1.0 spec at http://geojson.org/geojson-spec.html
2
+
3
+ module GeoRuby
4
+ #Raised when an error in the GeoJSON string is detected
5
+ class GeojsonFormatError < StandardError
6
+ end
7
+
8
+ # Class added to support geojson 'Feature' objects
9
+ class GeojsonFeature
10
+ attr_accessor :geometry, :properties, :id
11
+
12
+ def initialize(geometry, properties = {}, id = nil)
13
+ @geometry = geometry
14
+ @properties = properties
15
+ @id = id
16
+ end
17
+
18
+ def ==(other)
19
+ if (self.class != other.class)
20
+ false
21
+ else
22
+ (self.id == other.id) && (self.geometry == other.geometry) && (self.properties == other.properties)
23
+ end
24
+ end
25
+
26
+ def to_json(options={})
27
+ output = {}
28
+ output[:type] = 'Feature'
29
+ output[:geometry] = geometry
30
+ output[:properties] = properties
31
+ output[:id] = id unless id.nil?
32
+ output.to_json(options)
33
+ end
34
+ alias :as_geojson :to_json
35
+ end
36
+
37
+ # Class added to support geojson 'Feature Collection' objects
38
+ class GeojsonFeatureCollection
39
+ attr_accessor :features
40
+
41
+ def initialize(features)
42
+ @features = features
43
+ end
44
+
45
+ def ==(other)
46
+ if (self.class != other.class) || (features.size != other.features.size)
47
+ return false
48
+ else
49
+ features.each_index do |index|
50
+ return false if self.features[index] != other.features[index]
51
+ end
52
+ end
53
+ true
54
+ end
55
+
56
+ def to_json(options = {})
57
+ {:type => 'FeatureCollection',
58
+ :features => features}.to_json(options)
59
+ end
60
+ alias :as_geojson :to_json
61
+ end
62
+
63
+
64
+ class GeojsonParser
65
+ attr_reader :geometry
66
+
67
+ def parse(geojson, srid=DEFAULT_SRID)
68
+ @geometry = nil
69
+ geohash = JSON.parse(geojson)
70
+ parse_geohash(geohash, srid)
71
+ end
72
+
73
+ private
74
+
75
+ def parse_geohash(geohash, srid)
76
+ srid = srid_from_crs(geohash['crs']) || srid
77
+ case geohash['type']
78
+ when 'Point', 'MultiPoint', 'LineString', 'MultiLineString', 'Polygon', 'MultiPolygon', 'GeometryCollection'
79
+ @geometry = parse_geometry(geohash, srid)
80
+ when 'Feature'
81
+ @geometry = parse_geojson_feature(geohash, srid)
82
+ when 'FeatureCollection'
83
+ @geometry = parse_geojson_feature_collection(geohash, srid)
84
+ else
85
+ GeojsonFormatError.new('Unknown GeoJSON type')
86
+ end
87
+ end
88
+
89
+ def parse_geometry(geohash, srid)
90
+ srid = srid_from_crs(geohash['crs']) || srid
91
+ if geohash['type'] == 'GeometryCollection'
92
+ parse_geometry_collection(geohash, srid)
93
+ else
94
+ klass = GeoRuby::SimpleFeatures.const_get(geohash['type'])
95
+ klass.from_coordinates(geohash['coordinates'], srid, false, false)
96
+ end
97
+ end
98
+
99
+ def parse_geometry_collection(geohash, srid)
100
+ srid = srid_from_crs(geohash['crs']) || srid
101
+ geometries = geohash['geometries'].map{|g| parse_geometry(g,srid)}
102
+ GeometryCollection.from_geometries(geometries,srid)
103
+ end
104
+
105
+ def parse_geojson_feature(geohash, srid)
106
+ srid = srid_from_crs(geohash['crs']) || srid
107
+ geometry = parse_geometry(geohash['geometry'],srid)
108
+ GeojsonFeature.new(geometry, geohash['properties'], geohash['id'])
109
+ end
110
+
111
+ def parse_geojson_feature_collection(geohash, srid)
112
+ srid = srid_from_crs(geohash['crs']) || srid
113
+ features = []
114
+ geohash['features'].each do |feature|
115
+ features << parse_geojson_feature(feature, srid)
116
+ end
117
+ GeojsonFeatureCollection.new(features)
118
+ end
119
+
120
+ def srid_from_crs(crs)
121
+ # We somehow need to map crs to srid, currently only support for EPSG
122
+ if crs && crs['type'] == 'OGC'
123
+ urn = crs['properties']['urn'].split(':')
124
+ return urn.last if urn[4] == 'EPSG'
125
+ end
126
+ return nil
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,133 @@
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
+
13
+ #Raised when an error in the GeoRSS string is detected
14
+ class GeorssFormatError < StandardError
15
+ end
16
+
17
+ #Contains tags possibly found on GeoRss Simple geometries
18
+ class GeorssTags < Struct.new(:featuretypetag,:relationshiptag,:elev,:floor,:radius)
19
+ end
20
+
21
+ #Parses GeoRSS strings
22
+ #You can also use directly the static method Geometry.from_georss
23
+ class GeorssParser
24
+ attr_reader :georss_tags, :geometry
25
+
26
+ #Parses the georss geometry passed as argument and notifies the factory of events
27
+ #The parser assumes
28
+ def parse(georss,with_tags = false)
29
+ @geometry = nil
30
+ @georss_tags = GeorssTags.new
31
+ parse_geometry(georss,with_tags)
32
+ end
33
+
34
+ private
35
+ def parse_geometry(georss,with_tags)
36
+ georss.strip!
37
+ #check for W3CGeo first
38
+ if georss =~ /<[^:>]*:lat\s*>([^<]*)</
39
+ #if valid, it is W3CGeo
40
+ lat = $1.to_f
41
+ if georss =~ /<[^:>]*:long\s*>([^<]*)</
42
+ lon = $1.to_f
43
+ @geometry = Point.from_x_y(lon,lat)
44
+ else
45
+ raise GeorssFormatError.new("Bad W3CGeo GeoRSS format")
46
+ end
47
+ elsif georss =~ /^<\s*[^:>]*:where\s*>/
48
+ #GML format found
49
+ gml = $'.strip
50
+ if gml =~ /^<\s*[^:>]*:Point\s*>/
51
+ #gml point
52
+ if gml =~ /<\s*[^:>]*:pos\s*>([^<]*)/
53
+ point = $1.split(" ")
54
+ #lat comes first
55
+ @geometry = Point.from_x_y(point[1].to_f,point[0].to_f)
56
+ else
57
+ raise GeorssFormatError.new("Bad GML GeoRSS format: Malformed Point")
58
+ end
59
+ elsif gml =~ /^<\s*[^:>]*:LineString\s*>/
60
+ if gml =~ /<\s*[^:>]*:posList\s*>([^<]*)/
61
+ xy = $1.split(" ")
62
+ @geometry = LineString.new
63
+ 0.upto(xy.size/2 - 1) { |index| @geometry << Point.from_x_y(xy[index*2 + 1].to_f,xy[index*2].to_f)}
64
+ else
65
+ raise GeorssFormatError.new("Bad GML GeoRSS format: Malformed LineString")
66
+ end
67
+ elsif gml =~ /^<\s*[^:>]*:Polygon\s*>/
68
+ if gml =~ /<\s*[^:>]*:posList\s*>([^<]*)/
69
+ xy = $1.split(" ")
70
+ @geometry = Polygon.new
71
+ linear_ring = LinearRing.new
72
+ @geometry << linear_ring
73
+ xy = $1.split(" ")
74
+ 0.upto(xy.size/2 - 1) { |index| linear_ring << Point.from_x_y(xy[index*2 + 1].to_f,xy[index*2].to_f)}
75
+ else
76
+ raise GeorssFormatError.new("Bad GML GeoRSS format: Malformed Polygon")
77
+ end
78
+ elsif gml =~ /^<\s*[^:>]*:Envelope\s*>/
79
+ if gml =~ /<\s*[^:>]*:lowerCorner\s*>([^<]*)</
80
+ lc = $1.split(" ").collect { |x| x.to_f}.reverse
81
+ if gml =~ /<\s*[^:>]*:upperCorner\s*>([^<]*)</
82
+ uc = $1.split(" ").collect { |x| x.to_f}.reverse
83
+ @geometry = Envelope.from_coordinates([lc,uc])
84
+ else
85
+ raise GeorssFormatError.new("Bad GML GeoRSS format: Malformed Envelope")
86
+ end
87
+ else
88
+ raise GeorssFormatError.new("Bad GML GeoRSS format: Malformed Envelope")
89
+ end
90
+ else
91
+ raise GeorssFormatError.new("Bad GML GeoRSS format: Unknown geometry type")
92
+ end
93
+ else
94
+ #must be simple format
95
+ if georss =~ /^<\s*[^>:]*:point([^>]*)>(.*)</m
96
+ tags = $1
97
+ point = $2.gsub(","," ").split(" ")
98
+ @geometry = Point.from_x_y(point[1].to_f,point[0].to_f)
99
+ elsif georss =~ /^<\s*[^>:]*:line([^>]*)>(.*)</m
100
+ tags = $1
101
+ @geometry = LineString.new
102
+ xy = $2.gsub(","," ").split(" ")
103
+ 0.upto(xy.size/2 - 1) { |index| @geometry << Point.from_x_y(xy[index*2 + 1].to_f,xy[index*2].to_f)}
104
+ elsif georss =~ /^<\s*[^>:]*:polygon([^>]*)>(.*)</m
105
+ tags = $1
106
+ @geometry = Polygon.new
107
+ linear_ring = LinearRing.new
108
+ @geometry << linear_ring
109
+ xy = $2.gsub(","," ").split(" ")
110
+ 0.upto(xy.size/2 - 1) { |index| linear_ring << Point.from_x_y(xy[index*2 + 1].to_f,xy[index*2].to_f)}
111
+ elsif georss =~ /^<\s*[^>:]*:box([^>]*)>(.*)</m
112
+ tags = $1
113
+ corners = []
114
+ xy = $2.gsub(","," ").split(" ")
115
+ 0.upto(xy.size/2 - 1) {|index| corners << Point.from_x_y(xy[index*2 + 1].to_f,xy[index*2].to_f)}
116
+ @geometry = Envelope.from_points(corners)
117
+ else
118
+ raise GeorssFormatError.new("Bad Simple GeoRSS format: Unknown geometry type")
119
+ end
120
+
121
+ #geometry found: parse tags
122
+ return unless with_tags
123
+
124
+ @georss_tags.featuretypetag = $1 if tags =~ /featuretypetag=['"]([^"']*)['"]/
125
+ @georss_tags.relationshiptag = $1 if tags =~ /relationshiptag=['"]([^'"]*)['"]/
126
+ @georss_tags.elev = $1.to_f if tags =~ /elev=['"]([^'"]*)['"]/
127
+ @georss_tags.floor = $1.to_i if tags =~ /floor=['"]([^'"]*)['"]/
128
+ @georss_tags.radius = $1.to_f if tags =~ /radius=['"]([^'"]*)['"]/
129
+
130
+ end
131
+ end
132
+ end
133
+ end
@@ -32,36 +32,37 @@ module GeoRuby
32
32
  #Parses the ewkb string passed as argument and notifies the factory of events
33
33
  def parse(ewkb)
34
34
  @factory.reset
35
- @unpack_structure=UnpackStructure::new(ewkb)
36
35
  @with_z = false
37
36
  @with_m = false
37
+ @unpack_structure = UnpackStructure::new(ewkb)
38
38
  parse_geometry
39
39
  @unpack_structure.done
40
- @srid=nil
40
+ @srid = nil
41
41
  end
42
42
 
43
43
  private
44
+
44
45
  def parse_geometry
45
- @unpack_structure.endianness=@unpack_structure.read_byte
46
- @geometry_type = @unpack_structure.read_uint
46
+ @unpack_structure.endianness = @unpack_structure.read_byte
47
+ geometry_type = @unpack_structure.read_uint
47
48
 
48
- if (@geometry_type & Z_MASK) != 0
49
- @with_z=true
50
- @geometry_type = @geometry_type & ~Z_MASK
49
+ if (geometry_type & Z_MASK) != 0
50
+ @with_z = true
51
+ geometry_type = geometry_type & ~Z_MASK
51
52
  end
52
- if (@geometry_type & M_MASK) != 0
53
- @with_m=true
54
- @geometry_type = @geometry_type & ~M_MASK
53
+ if (geometry_type & M_MASK) != 0
54
+ @with_m = true
55
+ geometry_type = geometry_type & ~M_MASK
55
56
  end
56
- if (@geometry_type & SRID_MASK) != 0
57
+ if (geometry_type & SRID_MASK) != 0
57
58
  @srid = @unpack_structure.read_uint
58
- @geometry_type = @geometry_type & ~SRID_MASK
59
+ geometry_type = geometry_type & ~SRID_MASK
59
60
  else
60
61
  #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
61
- @srid= @srid || DEFAULT_SRID
62
+ @srid ||= DEFAULT_SRID
62
63
  end
63
64
 
64
- case @geometry_type
65
+ case geometry_type
65
66
  when 1
66
67
  parse_point
67
68
  when 2
@@ -129,8 +130,7 @@ module GeoRuby
129
130
 
130
131
  def parse_point
131
132
  @factory.begin_geometry(Point,@srid)
132
- x = @unpack_structure.read_double
133
- y = @unpack_structure.read_double
133
+ x, y = *@unpack_structure.read_point
134
134
  if ! (@with_z or @with_m) #most common case probably
135
135
  @factory.add_point_x_y(x,y)
136
136
  elsif @with_m and @with_z
@@ -151,56 +151,59 @@ module GeoRuby
151
151
 
152
152
  #Parses HexEWKB strings. In reality, it just transforms the HexEWKB string into the equivalent EWKB string and lets the EWKBParser do the actual parsing.
153
153
  class HexEWKBParser < EWKBParser
154
- def initialize(factory)
155
- super(factory)
156
- end
154
+
157
155
  #parses an HexEWKB string
158
156
  def parse(hexewkb)
159
157
  super(decode_hex(hexewkb))
160
158
  end
159
+
161
160
  #transforms a HexEWKB string into an EWKB string
162
161
  def decode_hex(hexewkb)
163
162
  [hexewkb].pack("H*")
164
163
  end
165
164
  end
166
165
 
167
- class HexEWKBParser < EWKBParser
168
- #transforms a HexEWKB string into an EWKB string
169
- # Patched for speedup
170
-
171
- end
172
-
173
166
  class UnpackStructure #:nodoc:
174
- NDR=1
175
- XDR=0
167
+ NDR = 1
168
+ XDR = 0
169
+
176
170
  def initialize(ewkb)
177
- @position=0
178
- @ewkb=ewkb
171
+ @position = 0
172
+ @ewkb = ewkb
179
173
  end
174
+
180
175
  def done
181
176
  raise EWKBFormatError::new("Trailing data") if @position != @ewkb.length
182
177
  end
178
+
179
+ def read_point
180
+ i = @position
181
+ @position += 16
182
+ raise EWKBFormatError::new("Truncated data") if @ewkb.length < @position
183
+ @ewkb.unpack("@#{i}#@double_mark#@double_mark@*")
184
+ end
185
+
183
186
  def read_double
184
- i=@position
187
+ i = @position
185
188
  @position += 8
186
- packed_double = @ewkb[i...@position]
187
- raise EWKBFormatError::new("Truncated data") if packed_double.nil? or packed_double.length < 8
188
- packed_double.unpack(@double_mark)[0]
189
+ raise EWKBFormatError::new("Truncated data") if @ewkb.length < @position
190
+ @ewkb.unpack("@#{i}#@double_mark@*").first
189
191
  end
192
+
190
193
  def read_uint
191
- i=@position
194
+ i = @position
192
195
  @position += 4
193
- packed_uint = @ewkb[i...@position]
194
- raise EWKBFormatError::new("Truncated data") if packed_uint.nil? or packed_uint.length < 4
195
- packed_uint.unpack(@uint_mark)[0]
196
+ raise EWKBFormatError::new("Truncated data") if @ewkb.length < @position
197
+ @ewkb.unpack("@#{i}#@uint_mark@*").first
196
198
  end
199
+
197
200
  def read_byte
198
201
  i = @position
199
202
  @position += 1
200
- packed_byte = @ewkb[i...@position]
201
- raise EWKBFormatError::new("Truncated data") if packed_byte.nil? or packed_byte.length < 1
202
- packed_byte.unpack("C")[0]
203
+ raise EWKBFormatError::new("Truncated data") if @ewkb.length < @position
204
+ @ewkb.unpack("@#{i}C@*").first
203
205
  end
206
+
204
207
  def endianness=(byte_order)
205
208
  if(byte_order == NDR)
206
209
  @uint_mark="V"