nofxx-georuby 1.7.3 → 1.9.0

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/README.rdoc ADDED
@@ -0,0 +1,184 @@
1
+ = GeoRuby
2
+
3
+
4
+ This is a fork of GeoRuby with some updates and extra funcionality.
5
+
6
+ It is intended as a holder for geometric data. The data model roughly
7
+ follows the OGC "Simple Features for SQL" specification, so it should
8
+ play nice with data returned from PostGIS or any Spatial Extensions (MongoDB, MySQL...).
9
+ Although without any kind of advanced functionalities (such as geometric operators or reprojections).
10
+ It also supports various output and input formats (GeoRSS, KML, Shapefile).
11
+
12
+ OGC:"http://www.opengis.org/docs/99-049.pdf"
13
+
14
+
15
+ == Available data types
16
+
17
+ The following geometric data types are provided :
18
+ - Point
19
+ - Line String
20
+ - Linear Ring
21
+ - Polygon
22
+ - Multi Point
23
+ - Multi Line String
24
+ - Multi Polygon
25
+ - Geometry Collection
26
+
27
+ They can be in 2D, 3DZ, 3DM, and 4D.
28
+
29
+ On top of this an Envelope class is available, to contain the bounding box of a geometry.
30
+
31
+ == Installation
32
+
33
+ To install the latest version, just type:
34
+
35
+ gem install nofxx-georuby
36
+
37
+
38
+ == Use
39
+
40
+
41
+ === Simple Examples
42
+
43
+ Creating a 3D Point:
44
+
45
+ Point.from_x_y_z(10.0, 20.0, 5.0)
46
+
47
+
48
+ Creating a LineString:
49
+
50
+ LineString.from_coordinates([[1,1],[2,2]],4326))
51
+
52
+
53
+ === Input and output
54
+
55
+ These geometries can be input and output in WKB/EWKB/WKT/EWKT format as well as
56
+ the related HexWKB and HexEWKB formats. HexEWKB and WKB are the default form under
57
+ which geometric data is returned respectively from PostGIS and MySql.
58
+
59
+ GeoRSS Simple, GeoRSS W3CGeo, GeoRSS GML can also be input and output.
60
+ Note that they will not output valid RSS, but just the part strictly concerning
61
+ the geometry as outlined in http://www.georss.org/1/ . Since the model does
62
+ not allow multiple geometries, for geometry collections, only the first geometry
63
+ will be output. Similarly, for polygons, the GeoRSS output will only contain the outer ring.
64
+ As for W3CGeo output, only points can be output, so the first point of the geometry is chosen.
65
+ By default the Simple format is output. Envelope can also be output in these formats:
66
+ The box geometric type is chosen (except for W3CGeo, where the center of the envelope is chose).
67
+ These formats can also be input and a GeoRuby geometry will be created.
68
+ Note that it will not read a valid RSS file, only a geometry string.
69
+
70
+ On top of that, there is now support for KML output and input.
71
+ As for GeoRSS, a valid KML file will not be output, but only the geometric data.
72
+ Options <tt>:id</tt>, <tt>:extrude</tt>, <tt>:tesselate</tt> and <tt>:altitude_mode</tt> can be given.
73
+ Note that if the <tt>:altitude_mode</tt> option is not passed or set to <tt>clampToGround</tt>,
74
+ the altitude data will not be output even if present. Envelopes output a LatLonAltBox instead of a geometry.
75
+ For the output, the following geometric types are supported : Point, LineString, Polygon.
76
+
77
+
78
+ === SHP reading et writing
79
+
80
+ Georuby has support for reading ESRI shapefiles (http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf).
81
+ A tool called <tt>shp2sql.rb</tt> is also provided: it shows how to use the SHP reading functionality together
82
+ with the spatial adapter plugin for Rails to import spatial features into MySQL and PostGIS.
83
+
84
+ Here is an example of Shapefile reading, that goes through all the geometries
85
+ in a file and disaply the values of the attributes :
86
+
87
+ require 'geo_ruby/shp'
88
+
89
+ ShpFile.open(shpfile) do |shp|
90
+ shp.each do |shape|
91
+ geom = shape.geometry #a GeoRuby SimpleFeature
92
+ att_data = shape.data #a Hash
93
+ shp.fields.each do |field|
94
+ puts att_data[field.name]
95
+ end
96
+ end
97
+ end
98
+
99
+ Support for ESRI shapefile creation and modification has been added as well.
100
+ New shapefiles can be created given a geometry type and specifications for the DBF fields.
101
+ Data can be added and removed from an existing shapefile.
102
+ An update operation is also provided for convenience: it just performs a 'delete' and an 'add',
103
+ which means the index of the modified record will change. Note that once a shapefile has been created,
104
+ GeoRuby does not allow the modification of the schema (it will probably be done in a subsequent version).
105
+
106
+ Here is an example of how to create a new Shapefile with 2 fields :
107
+
108
+ shpfile = ShpFile.create('hello.shp',ShpType::POINT,[Dbf::Field.new("Hoyoyo","C",10),Dbf::Field.new("Boyoul","N",10,0)])
109
+
110
+ The file is then open for reading and writing.
111
+
112
+ Here is an example of how to write to a shapefile (created or not with GeoRuby) :
113
+
114
+ shpfile = ShpFile.open('places.shp')
115
+ shpfile.transaction do |tr|
116
+ tr.add(ShpRecord.new(Point.from_x_y(123.4,123.4),'Hoyoyo' => "AEZ",'Bouyoul' => 45))
117
+ tr.update(4,ShpRecord.new(Point.from_x_y(-16.67,16.41),'Hoyoyo' => "EatMe",'Bouyoul' => 42))
118
+ tr.delete(1)
119
+ end
120
+ shpfile.close
121
+
122
+ Note the transaction is just there so the operations on the files can be buffered.
123
+ Nothing happens on the original files until the block has finished executing.
124
+ Calling <tt>tr.rollback</tt> at anytime during the execution will prevent the modifications.
125
+
126
+ Also currently, error reporting is minimal and it has not been tested that
127
+ thoroughly so caveat emptor and backup before performing any destructive operation.
128
+
129
+
130
+ === GPX Reading
131
+
132
+ You can read and convert GPX Files to LineString/Polygon:
133
+
134
+ gpxfile = GpxFile.open(tour.gpx')
135
+ gpxfile.as_line_string
136
+ => GeoRuby::SimpleFeatures::LineString..
137
+
138
+ === GeoJSON Support
139
+ Basic GeoJSON support has been implemented per v1.0 of the {spec}[http://geojson.org/geojson-spec.html].
140
+
141
+ USAGE:
142
+
143
+ input - GeoRuby::SimpleFeatures::Geometry.from_geojson(geojson_string)
144
+ output - call #as_geojson or #to_json on any SimpleFeature Geometry instance
145
+
146
+ TODO:
147
+ * Refactor to support extremely large GeoJSON input streams / files. Currently
148
+ the entire GeoJSON representation must be loaded into memory as a String
149
+ * Improve srid/crs support on input and add support on output
150
+ * Implement bounding-box spport per spec on input/output?
151
+ * Improved / more tests
152
+
153
+ GeoJSON support implemented by {Marcus Mateus}[http://github.com/marcusmateus] and released courtesy {SimpliTex}[http://simplitex.com].
154
+
155
+ === Acknowledgement
156
+
157
+ The SHP reading part uses the DBF library (http://rubyforge.org/projects/dbf/) by Keith Morrison (http://infused.org).
158
+ Thanks also to Pramukta Kumar and Pete Schwamb for their contributions.
159
+
160
+ === License
161
+
162
+ GeoRuby is released under the MIT license.
163
+
164
+
165
+ == Support (Original GeoRuby gem)
166
+
167
+ Any questions, enhancement proposals, bug notifications or corrections
168
+ can be sent to mailto:guilhem.vellut@gmail.com.
169
+
170
+
171
+ === Changes since the last version
172
+
173
+ - Writing of ESRI shapefiles
174
+ - Reading of ESRI shapefiles
175
+ - Addition of a small tool to import spatial features in MySQL and PostGIS from a SHP file
176
+
177
+
178
+ === Coming in the next versions
179
+
180
+ - Schema modification of existing shapefiles
181
+ - More error reporting when writing shapefiles
182
+ - More tests on writing shapefiles ; tests on real-world shapefiles
183
+ - Better shp2sql import tool
184
+ - Documentation
data/Rakefile CHANGED
@@ -7,11 +7,12 @@ begin
7
7
  gem.name = "nofxx-georuby"
8
8
  gem.summary = "Ruby data holder for OGC Simple Features"
9
9
  gem.description = "GeoRuby provides geometric data types from the OGC 'Simple Features' specification."
10
- gem.email = "x@nofxx.com"
10
+ gem.email = "georuby@simplitex.com"
11
11
  gem.homepage = "http://github.com/nofxx/georuby"
12
- gem.authors = ["Guilhem Vellut", "Marcos Piccinini"]
13
- gem.add_development_dependency "rspec", ">= 1.2.9"
14
- gem.add_development_dependency "dbf", ">= 1.1.2"
12
+ gem.authors = ["Guilhem Vellut", "Marcos Piccinini", "Marcus Mateus", "Doug Cole"]
13
+ gem.add_runtime_dependency "json_pure", ">=1.4.6"
14
+ gem.add_development_dependency "rspec", ">= 2.0.0"
15
+ gem.add_development_dependency "dbf", ">= 1.2.9"
15
16
  end
16
17
  rescue LoadError
17
18
  puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.7.3
1
+ 1.9.0
data/lib/geo_ruby.rb CHANGED
@@ -16,6 +16,7 @@ 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
18
  require 'geo_ruby/simple_features/georss_parser'
19
+ require 'geo_ruby/simple_features/geojson_parser'
19
20
 
20
21
  # Include if you need
21
22
  # require 'geo_ruby/shp4r/shp'
@@ -50,7 +50,7 @@ module GeoRuby
50
50
 
51
51
  #Sends back the center of the envelope
52
52
  def center
53
- Point.from_x_y((lower_corner.x + upper_corner.x)/2,(lower_corner.y + upper_corner.y)/2)
53
+ Point.from_x_y((lower_corner.x + upper_corner.x)/2,(lower_corner.y + upper_corner.y)/2, srid)
54
54
  end
55
55
 
56
56
  #Zoom level
@@ -27,15 +27,6 @@ module GeoRuby
27
27
 
28
28
  def initialize(factory)
29
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
30
  end
40
31
 
41
32
  #Parses the ewkb string passed as argument and notifies the factory of events
@@ -70,8 +61,21 @@ module GeoRuby
70
61
  @srid= @srid || DEFAULT_SRID
71
62
  end
72
63
 
73
- if @parse_options.has_key? @geometry_type
74
- @parse_options[@geometry_type].call
64
+ case @geometry_type
65
+ when 1
66
+ parse_point
67
+ when 2
68
+ parse_line_string
69
+ when 3
70
+ parse_polygon
71
+ when 4
72
+ parse_multi_point
73
+ when 5
74
+ parse_multi_line_string
75
+ when 6
76
+ parse_multi_polygon
77
+ when 7
78
+ parse_geometry_collection
75
79
  else
76
80
  raise EWKBFormatError::new("Unknown geometry type")
77
81
  end
@@ -156,12 +160,7 @@ module GeoRuby
156
160
  end
157
161
  #transforms a HexEWKB string into an EWKB string
158
162
  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
163
+ [hexewkb].pack("H*")
165
164
  end
166
165
  end
167
166
 
@@ -0,0 +1,132 @@
1
+ # GeoJSON parser based on the v1.0 spec at http://geojson.org/geojson-spec.html
2
+ require 'json/pure'
3
+
4
+ module GeoRuby
5
+ module SimpleFeatures
6
+ #Raised when an error in the GeoJSON string is detected
7
+ class GeojsonFormatError < StandardError
8
+ end
9
+
10
+ # Class added to support geojson 'Feature' objects
11
+ class GeojsonFeature
12
+ attr_accessor :geometry, :properties, :id
13
+
14
+ def initialize(geometry, properties = {}, id = nil)
15
+ @geometry = geometry
16
+ @properties = properties
17
+ @id = id
18
+ end
19
+
20
+ def ==(other)
21
+ if (self.class != other.class)
22
+ false
23
+ else
24
+ (self.id == other.id) && (self.geometry == other.geometry) && (self.properties == other.properties)
25
+ end
26
+ end
27
+
28
+ def to_json(options={})
29
+ output = {}
30
+ output[:type] = 'Feature'
31
+ output[:geometry] = geometry
32
+ output[:properties] = properties
33
+ output[:id] = id unless id.nil?
34
+ output.to_json(options)
35
+ end
36
+ alias :as_geojson :to_json
37
+ end
38
+
39
+ # Class added to support geojson 'Feature Collection' objects
40
+ class GeojsonFeatureCollection
41
+ attr_accessor :features
42
+
43
+ def initialize(features)
44
+ @features = features
45
+ end
46
+
47
+ def ==(other)
48
+ if (self.class != other.class) || (features.size != other.features.size)
49
+ return false
50
+ else
51
+ features.each_index do |index|
52
+ return false if self.features[index] != other.features[index]
53
+ end
54
+ end
55
+ true
56
+ end
57
+
58
+ def to_json(options = {})
59
+ {:type => 'FeatureCollection',
60
+ :features => features}.to_json(options)
61
+ end
62
+ alias :as_geojson :to_json
63
+ end
64
+
65
+
66
+ class GeojsonParser
67
+ attr_reader :geometry
68
+
69
+ def parse(geojson, srid=DEFAULT_SRID)
70
+ @geometry = nil
71
+ geohash = JSON.parse(geojson)
72
+ parse_geohash(geohash, srid)
73
+ end
74
+
75
+ private
76
+
77
+ def parse_geohash(geohash, srid)
78
+ srid = srid_from_crs(geohash['crs']) || srid
79
+ case geohash['type']
80
+ when 'Point', 'MultiPoint', 'LineString', 'MultiLineString', 'Polygon', 'MultiPolygon', 'GeometryCollection'
81
+ @geometry = parse_geometry(geohash, srid)
82
+ when 'Feature'
83
+ @geometry = parse_geojson_feature(geohash, srid)
84
+ when 'FeatureCollection'
85
+ @geometry = parse_geojson_feature_collection(geohash, srid)
86
+ else
87
+ GeojsonFormatError.new('Unknown GeoJSON type')
88
+ end
89
+ end
90
+
91
+ def parse_geometry(geohash, srid)
92
+ srid = srid_from_crs(geohash['crs']) || srid
93
+ if geohash['type'] == 'GeometryCollection'
94
+ parse_geometry_collection(geohash, srid)
95
+ else
96
+ klass = GeoRuby::SimpleFeatures.const_get(geohash['type'])
97
+ klass.from_coordinates(geohash['coordinates'], srid, false, false)
98
+ end
99
+ end
100
+
101
+ def parse_geometry_collection(geohash, srid)
102
+ srid = srid_from_crs(geohash['crs']) || srid
103
+ geometries = geohash['geometries'].map{|g| parse_geometry(g,srid)}
104
+ GeometryCollection.from_geometries(geometries,srid)
105
+ end
106
+
107
+ def parse_geojson_feature(geohash, srid)
108
+ srid = srid_from_crs(geohash['crs']) || srid
109
+ geometry = parse_geometry(geohash['geometry'],srid)
110
+ GeojsonFeature.new(geometry, geohash['properties'], geohash['id'])
111
+ end
112
+
113
+ def parse_geojson_feature_collection(geohash, srid)
114
+ srid = srid_from_crs(geohash['crs']) || srid
115
+ features = []
116
+ geohash['features'].each do |feature|
117
+ features << parse_geojson_feature(feature, srid)
118
+ end
119
+ GeojsonFeatureCollection.new(features)
120
+ end
121
+
122
+ def srid_from_crs(crs)
123
+ # We somehow need to map crs to srid, currently only support for EPSG
124
+ if crs && crs['type'] == 'OGC'
125
+ urn = crs['properties']['urn'].split(':')
126
+ return urn.last if urn[4] == 'EPSG'
127
+ end
128
+ return nil
129
+ end
130
+ end
131
+ end
132
+ end
@@ -215,6 +215,14 @@ module GeoRuby#:nodoc:
215
215
  end
216
216
  return wkt
217
217
  end
218
+
219
+ # Some GeoJSON files do not include srid info, so
220
+ # we provide an optional parameter
221
+ def self.from_geojson(geojson, srid=DEFAULT_SRID)
222
+ geojson_parser= GeojsonParser::new
223
+ geojson_parser.parse(geojson, srid)
224
+ geojson_parser.geometry
225
+ end
218
226
 
219
227
  private
220
228
 
@@ -102,6 +102,14 @@ module GeoRuby
102
102
  "GEOMETRYCOLLECTION"
103
103
  end
104
104
 
105
+ # simple geojson representation
106
+ # TODO add CRS / SRID support?
107
+ def to_json(options = {})
108
+ {:type => 'GeometryCollection',
109
+ :geometries => self.geometries}.to_json(options)
110
+ end
111
+ alias :as_geojson :to_json
112
+
105
113
  #georss simple representation : outputs only the first geometry of the collection
106
114
  def georss_simple_representation(options)#:nodoc:
107
115
  self[0].georss_simple_representation(options)
@@ -188,6 +188,18 @@ module GeoRuby
188
188
  end
189
189
  end
190
190
 
191
+ def to_coordinates
192
+ points.map{|p| p.to_coordinates }
193
+ end
194
+
195
+ # simple geojson representation
196
+ # TODO add CRS / SRID support?
197
+ def to_json(options = {})
198
+ {:type => 'LineString',
199
+ :coordinates => self.to_coordinates}.to_json(options)
200
+ end
201
+ alias :as_geojson :to_json
202
+
191
203
  #Creates a new line string. Accept an array of points as argument
192
204
  def self.from_points(points,srid=DEFAULT_SRID,with_z=false,with_m=false)
193
205
  line_string = new(srid,with_z,with_m)