nofxx-georuby 1.7.3 → 1.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +184 -0
- data/Rakefile +5 -4
- data/VERSION +1 -1
- data/lib/geo_ruby.rb +1 -0
- data/lib/geo_ruby/simple_features/envelope.rb +1 -1
- data/lib/geo_ruby/simple_features/ewkb_parser.rb +16 -17
- data/lib/geo_ruby/simple_features/geojson_parser.rb +132 -0
- data/lib/geo_ruby/simple_features/geometry.rb +8 -0
- data/lib/geo_ruby/simple_features/geometry_collection.rb +8 -0
- data/lib/geo_ruby/simple_features/line_string.rb +12 -0
- data/lib/geo_ruby/simple_features/multi_line_string.rb +12 -0
- data/lib/geo_ruby/simple_features/multi_point.rb +12 -0
- data/lib/geo_ruby/simple_features/multi_polygon.rb +12 -0
- data/lib/geo_ruby/simple_features/point.rb +17 -0
- data/lib/geo_ruby/simple_features/polygon.rb +12 -0
- data/marcusmateus-georuby.gemspec +136 -0
- data/spec/data/geojson/feature_collection.json +34 -0
- data/spec/geo_ruby/gpx4r/gpx_spec.rb +10 -10
- data/spec/geo_ruby/shp4r/shp_spec.rb +4 -4
- data/spec/geo_ruby/simple_features/envelope_spec.rb +3 -1
- data/spec/geo_ruby/simple_features/geojson_parser_spec.rb +147 -0
- data/spec/geo_ruby/simple_features/geometry_spec.rb +1 -1
- data/spec/geo_ruby/simple_features/point_spec.rb +27 -27
- data/spec/spec_helper.rb +13 -6
- metadata +51 -31
- data/.gitignore +0 -6
- data/README.txt +0 -118
- data/nofxx-georuby.gemspec +0 -132
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 = "
|
10
|
+
gem.email = "georuby@simplitex.com"
|
11
11
|
gem.homepage = "http://github.com/nofxx/georuby"
|
12
|
-
gem.authors = ["Guilhem Vellut", "Marcos Piccinini"]
|
13
|
-
gem.
|
14
|
-
gem.add_development_dependency "
|
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.
|
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
|
-
|
74
|
-
|
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
|
-
|
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)
|