nofxx-georuby 1.9.0 → 1.9.1

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/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"