georuby 1.9.7 → 1.9.8
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 +7 -5
- data/lib/geo_ruby.rb +5 -4
- data/lib/geo_ruby/kml.rb +84 -0
- data/lib/geo_ruby/simple_features/geometry.rb +4 -39
- data/lib/geo_ruby/simple_features/linear_ring.rb +19 -14
- data/lib/geo_ruby/version.rb +1 -1
- data/spec/geo_ruby/kml_spec.rb +79 -0
- data/spec/spec_helper.rb +1 -0
- metadata +20 -8
data/README.rdoc
CHANGED
@@ -1,9 +1,7 @@
|
|
1
1
|
= GeoRuby
|
2
2
|
|
3
3
|
|
4
|
-
This is a
|
5
|
-
|
6
|
-
It is intended as a holder for geometric data. The data model roughly
|
4
|
+
This is intended as a holder for geometric data. The data model roughly
|
7
5
|
follows the OGC "Simple Features for SQL" specification, so it should
|
8
6
|
play nice with data returned from PostGIS or any Spatial Extensions (MongoDB, MySQL...).
|
9
7
|
Although without any kind of advanced functionalities (such as geometric operators or reprojections).
|
@@ -12,7 +10,7 @@ It also supports various output and input formats (GeoRSS, KML, Shapefile).
|
|
12
10
|
OGC:"http://www.opengis.org/docs/99-049.pdf"
|
13
11
|
|
14
12
|
GeoRuby is written in pure Ruby.
|
15
|
-
If you are looking for Proj/Geos/ext rubygem checkout:
|
13
|
+
If you are looking for Proj/Geos/ext C rubygem checkout:
|
16
14
|
rgeo:"https://github.com/dazuma/rgeo"
|
17
15
|
|
18
16
|
|
@@ -36,7 +34,11 @@ On top of this an Envelope class is available, to contain the bounding box of a
|
|
36
34
|
|
37
35
|
To install the latest version, just type:
|
38
36
|
|
39
|
-
gem install
|
37
|
+
gem install georuby
|
38
|
+
|
39
|
+
Or include on your projects`s Gemfile:
|
40
|
+
|
41
|
+
gem 'georuby'
|
40
42
|
|
41
43
|
|
42
44
|
== Use
|
data/lib/geo_ruby.rb
CHANGED
@@ -17,7 +17,8 @@ require 'geo_ruby/simple_features/envelope'
|
|
17
17
|
require 'geo_ruby/simple_features/geometry_factory'
|
18
18
|
|
19
19
|
# Require if you need
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
require 'geo_ruby/shp4r/shp'
|
21
|
+
require 'geo_ruby/gpx4r/gpx'
|
22
|
+
require 'geo_ruby/geojson'
|
23
|
+
require 'geo_ruby/georss'
|
24
|
+
require 'geo_ruby/kml'
|
data/lib/geo_ruby/kml.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'rexml/parsers/pullparser'
|
2
|
+
module GeoRuby
|
3
|
+
class KmlParser
|
4
|
+
ELEMENT_MAP = {
|
5
|
+
# "Point" => SimpleFeatures::Point, # we don't need to map points. they are done automatically by the coordinate parsing
|
6
|
+
"LineString" => SimpleFeatures::LineString,
|
7
|
+
"LinearRing" => SimpleFeatures::LinearRing,
|
8
|
+
"Polygon" => SimpleFeatures::Polygon,
|
9
|
+
"MultiGeometry" => SimpleFeatures::GeometryCollection
|
10
|
+
}
|
11
|
+
def initialize(factory)
|
12
|
+
@factory = factory
|
13
|
+
@buffer = ""
|
14
|
+
@with_z = false
|
15
|
+
end
|
16
|
+
# argument should be a valid kml geometry fragment ie. <Point> .... </Point>
|
17
|
+
# returns the GeoRuby geometry object back
|
18
|
+
def parse(kml)
|
19
|
+
@factory.reset
|
20
|
+
@with_z = false
|
21
|
+
@parser = REXML::Parsers::PullParser.new(kml)
|
22
|
+
while @parser.has_next?
|
23
|
+
e = @parser.pull
|
24
|
+
if e.start_element?
|
25
|
+
if(type = ELEMENT_MAP[e[0]])
|
26
|
+
@factory.begin_geometry(type)
|
27
|
+
else
|
28
|
+
@buffer = "" if(e[0] == "coordinates") # clear the buffer
|
29
|
+
accumulate_start(e)
|
30
|
+
end
|
31
|
+
elsif e.end_element?
|
32
|
+
if(ELEMENT_MAP[e[0]])
|
33
|
+
@factory.end_geometry(@with_z)
|
34
|
+
@buffer = "" # clear the buffer
|
35
|
+
else
|
36
|
+
accumulate_end(e)
|
37
|
+
if(e[0] == "coordinates")
|
38
|
+
parse_coordinates(@buffer)
|
39
|
+
@buffer = "" # clear the buffer
|
40
|
+
end
|
41
|
+
end
|
42
|
+
elsif e.text?
|
43
|
+
accumulate_text(e)
|
44
|
+
elsif e.cdata?
|
45
|
+
accumulate_cdata(e)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
@factory.geometry.dup
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
def accumulate_text(e); @buffer << e[0]; end
|
53
|
+
def accumulate_cdata(e); @buffer << "<![CDATA[#{e[0]}]]>"; end
|
54
|
+
def accumulate_start(e)
|
55
|
+
@buffer << "<#{e[0]}"
|
56
|
+
if(e[1].class == Hash)
|
57
|
+
e[1].each_pair {|k,v| @buffer << " #{k}='#{v}'" }
|
58
|
+
end
|
59
|
+
@buffer << ">"
|
60
|
+
end
|
61
|
+
def accumulate_end(e); @buffer << "</#{e[0]}>"; end
|
62
|
+
|
63
|
+
def parse_coordinates(buffer)
|
64
|
+
if(buffer =~ /<coordinates>(.+)<\/coordinates>/)
|
65
|
+
$1.gsub(/\n/, " ").strip.split(/\s+/).each do |coord|
|
66
|
+
x,y,z = coord.split(',')
|
67
|
+
if(x.nil? || y.nil?)
|
68
|
+
raise StandardError, "coordinates must have at least x and y elements"
|
69
|
+
end
|
70
|
+
@factory.begin_geometry(SimpleFeatures::Point)
|
71
|
+
if(z.nil?)
|
72
|
+
@factory.add_point_x_y(x,y)
|
73
|
+
else
|
74
|
+
@factory.add_point_x_y_z(x,y,z)
|
75
|
+
@with_z = true unless @with_z # is the conditional even necessary
|
76
|
+
end
|
77
|
+
@factory.end_geometry(@with_z)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
rescue
|
81
|
+
raise StandardError, "error parsing coordinates: check your kml for errors"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -175,47 +175,12 @@ module GeoRuby#:nodoc:
|
|
175
175
|
end
|
176
176
|
|
177
177
|
#Sends back a geometry from a KML encoded geometry string.
|
178
|
-
#Limitations : Only supports points, linestrings and polygons (no collection for now).
|
179
|
-
#Addapted from Pramukta's code
|
180
178
|
def self.from_kml(kml)
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
require 'rexml/document'
|
185
|
-
def self.kml_to_wkt(kml)
|
186
|
-
doc = REXML::Document.new(kml)
|
187
|
-
wkt = ""
|
188
|
-
if ["Point", "LineString", "Polygon" ].include?(doc.root.name)
|
189
|
-
case doc.root.name
|
190
|
-
when "Point" then
|
191
|
-
coords = doc.elements["/Point/coordinates"].text.gsub(/\n/," ")
|
192
|
-
wkt = doc.root.name.upcase + "(" + split_coords(coords).join(' ') + ")"
|
193
|
-
when "LineString" then
|
194
|
-
coords = doc.elements["/LineString/coordinates"].text.gsub(/\n/," ")
|
195
|
-
coords = split_coords(coords)
|
196
|
-
wkt = doc.root.name.upcase + "(" + coords.join(",") + ")"
|
197
|
-
when "Polygon" then
|
198
|
-
# polygons have one outer ring and zero or more inner rings
|
199
|
-
bounds = []
|
200
|
-
bounds << doc.elements["/Polygon/outerBoundaryIs/LinearRing/coordinates"].text
|
201
|
-
inner_coords_elements = doc.elements.each("/Polygon/innerBoundaryIs/LinearRing/coordinates") do |inner_coords|
|
202
|
-
inner_coords = inner_coords.text
|
203
|
-
bounds << inner_coords
|
204
|
-
end
|
205
|
-
|
206
|
-
wkt = doc.root.name.upcase + "(" + bounds.map do |bound|
|
207
|
-
bound.gsub!(/\n/, " ")
|
208
|
-
bound = split_coords(bound)
|
209
|
-
if bound.first != bound.last
|
210
|
-
bound.push bound.first
|
211
|
-
end
|
212
|
-
"(" + bound.join(",") + ")"
|
213
|
-
end.join(",") + ")"
|
214
|
-
end
|
215
|
-
end
|
216
|
-
return wkt
|
179
|
+
factory = GeometryFactory.new
|
180
|
+
parser = KmlParser.new(factory)
|
181
|
+
parser.parse(kml)
|
217
182
|
end
|
218
|
-
|
183
|
+
|
219
184
|
# Some GeoJSON files do not include srid info, so
|
220
185
|
# we provide an optional parameter
|
221
186
|
def self.from_geojson(geojson, srid=DEFAULT_SRID)
|
@@ -12,21 +12,26 @@ module GeoRuby
|
|
12
12
|
super(srid,with_z,with_m)
|
13
13
|
end
|
14
14
|
|
15
|
+
# fix kml export
|
16
|
+
alias_method :orig_kml_representation, :kml_representation
|
17
|
+
def kml_representation(options = {})
|
18
|
+
orig_kml_representation(options).gsub('LineString', 'LinearRing')
|
19
|
+
end
|
15
20
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
21
|
+
# Does this linear string contain the given point? We use the
|
22
|
+
# algorithm described here:
|
23
|
+
#
|
24
|
+
# http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
|
25
|
+
def contains_point?(point)
|
26
|
+
x, y = point.x, point.y
|
27
|
+
tuples = @points.zip(@points[1..-1] + [@points[0]])
|
28
|
+
crossings =
|
29
|
+
tuples.select do |a, b|
|
30
|
+
(b.y > y != a.y > y) && (x < (a.x - b.x) * (y - b.y) / (a.y - b.y) + b.x)
|
31
|
+
end
|
32
|
+
|
33
|
+
crossings.size % 2 == 1
|
34
|
+
end
|
30
35
|
end
|
31
36
|
|
32
37
|
end
|
data/lib/geo_ruby/version.rb
CHANGED
@@ -0,0 +1,79 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe KmlParser do
|
4
|
+
before(:all) do
|
5
|
+
POINT = "<Point><coordinates>-82.4898187291883,34.2473206042649</coordinates></Point>"
|
6
|
+
LINESTRING = "<LineString><coordinates>-122.365662,37.826988 -122.365202,37.826302 -122.364581,37.82655 -122.365038,37.827237</coordinates></LineString>"
|
7
|
+
LINEARRING = "<LinearRing><coordinates>-122.365662,37.826988 -122.365202,37.826302 -122.364581,37.82655 -122.365038,37.827237 -122.365662,37.826988</coordinates></LinearRing>"
|
8
|
+
POLYGON = "<Polygon><outerBoundaryIs><LinearRing><coordinates>-82.5961385808407,34.0134202383713 -82.6029437979289,34.0346366848087 -82.6603553035687,34.1083560439036 -82.7357807829899,34.1697961502507 -82.7425935601244,34.2055536194311 -82.3145921793097,34.4812209701586 -82.2758648198483,34.4347213073308 -82.2405017851073,34.4067761024174 -82.3327002190662,34.3417863447576 -82.2910826671599,34.2708004396966 -82.2497468801283,34.2261551348023 -82.2370438982521,34.1709424545969 -82.2569955519648,34.1119142196088 -82.3237086862075,34.0601294413679 -82.368425596693,34.0533120146082 -82.4455985300521,34.0562556252352 -82.4806178108032,34.0759686807282 -82.5334224196077,34.0620944842448 -82.5961385808407,34.0134202383713</coordinates></LinearRing></outerBoundaryIs></Polygon>"
|
9
|
+
COMPLEX_POLYGON = "<Polygon><outerBoundaryIs><LinearRing><coordinates>-122.366278,37.818844 -122.365248,37.819267 -122.365640,37.819861 -122.366669,37.819429 -122.366278,37.818844</coordinates></LinearRing></outerBoundaryIs><innerBoundaryIs><LinearRing><coordinates>-122.366212,37.818977 -122.365424,37.819294 -122.365704,37.819731 -122.366488,37.819402 -122.366212,37.818977</coordinates></LinearRing></innerBoundaryIs></Polygon>"
|
10
|
+
MULTIGEOMETRY = "<MultiGeometry><Polygon><outerBoundaryIs><LinearRing><coordinates>-82.5961385808407,34.0134202383713 -82.6029437979289,34.0346366848087 -82.6603553035687,34.1083560439036 -82.7357807829899,34.1697961502507 -82.7425935601244,34.2055536194311 -82.3145921793097,34.4812209701586 -82.2758648198483,34.4347213073308 -82.2405017851073,34.4067761024174 -82.3327002190662,34.3417863447576 -82.2910826671599,34.2708004396966 -82.2497468801283,34.2261551348023 -82.2370438982521,34.1709424545969 -82.2569955519648,34.1119142196088 -82.3237086862075,34.0601294413679 -82.368425596693,34.0533120146082 -82.4455985300521,34.0562556252352 -82.4806178108032,34.0759686807282 -82.5334224196077,34.0620944842448 -82.5961385808407,34.0134202383713</coordinates></LinearRing></outerBoundaryIs></Polygon><Point><coordinates>-82.4898187291883,34.2473206042649</coordinates></Point></MultiGeometry>"
|
11
|
+
|
12
|
+
POINT3D = "<Point><coordinates>-82.4898187291883,34.2473206042649,5</coordinates></Point>"
|
13
|
+
LINESTRING3D = "<LineString><coordinates>-122.365662,37.826988,1 -122.365202,37.826302,2 -122.364581,37.82655,3 -122.365038,37.827237,4</coordinates></LineString>"
|
14
|
+
end
|
15
|
+
|
16
|
+
before(:each) do
|
17
|
+
@factory = GeometryFactory.new
|
18
|
+
@kml_parser = KmlParser.new(@factory)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should parse a Point correctly" do
|
22
|
+
@kml_parser.parse(POINT)
|
23
|
+
g = @factory.geometry
|
24
|
+
g.should_not eql(nil)
|
25
|
+
g.should be_an_instance_of(GeoRuby::SimpleFeatures::Point)
|
26
|
+
g.as_kml.gsub(/\n/,'').should eql(POINT)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should parse a LineString correctly" do
|
30
|
+
@kml_parser.parse(LINESTRING)
|
31
|
+
g = @factory.geometry
|
32
|
+
g.should_not eql(nil)
|
33
|
+
g.should be_an_instance_of(GeoRuby::SimpleFeatures::LineString)
|
34
|
+
g.as_kml.gsub(/\n/,'').should eql(LINESTRING)
|
35
|
+
|
36
|
+
@kml_parser.parse(LINEARRING)
|
37
|
+
g = @factory.geometry
|
38
|
+
g.should_not eql(nil)
|
39
|
+
g.should be_an_instance_of(GeoRuby::SimpleFeatures::LinearRing)
|
40
|
+
g.as_kml.gsub(/\n/,'').should eql(LINEARRING)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should parse a Polygon correctly" do
|
44
|
+
@kml_parser.parse(POLYGON)
|
45
|
+
g = @factory.geometry
|
46
|
+
g.should_not eql(nil)
|
47
|
+
g.should be_an_instance_of(GeoRuby::SimpleFeatures::Polygon)
|
48
|
+
g.as_kml.gsub(/\n/,'').should eql(POLYGON)
|
49
|
+
|
50
|
+
@kml_parser.parse(COMPLEX_POLYGON)
|
51
|
+
g = @factory.geometry
|
52
|
+
g.should_not eql(nil)
|
53
|
+
g.should be_an_instance_of(GeoRuby::SimpleFeatures::Polygon)
|
54
|
+
g.as_kml.gsub(/\n/,'').should eql(COMPLEX_POLYGON)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should parse a MultiGeometry correctly" do
|
58
|
+
@kml_parser.parse(MULTIGEOMETRY)
|
59
|
+
g = @factory.geometry
|
60
|
+
g.should_not eql(nil)
|
61
|
+
g.geometries.length.should eql(2)
|
62
|
+
g.should be_an_instance_of(GeoRuby::SimpleFeatures::GeometryCollection)
|
63
|
+
g.as_kml.gsub(/\n/,'').should eql(MULTIGEOMETRY)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should parse 3D geometries correctly" do
|
67
|
+
# not testing generation because GeoRuby kml generation logic currently requires additional
|
68
|
+
# XML nodes to actually output 3D coordinate information. I might modify that behavior
|
69
|
+
g = @kml_parser.parse(POINT3D)
|
70
|
+
g.should_not eql(nil)
|
71
|
+
g.with_z.should eql(true)
|
72
|
+
# g.as_kml(:altitude_mode => "clampToGround").gsub(/\n/,'').should eql(POINT3D)
|
73
|
+
|
74
|
+
g = @kml_parser.parse(LINESTRING3D)
|
75
|
+
g.should_not eql(nil)
|
76
|
+
g.with_z.should eql(true)
|
77
|
+
# g.as_kml(:altitude_mode => "clampToGround").gsub(/\n/,'').should eql(LINESTRING3D)
|
78
|
+
end
|
79
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: georuby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.9.
|
4
|
+
version: 1.9.8
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -12,11 +12,11 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2012-
|
15
|
+
date: 2012-09-09 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: dbf
|
19
|
-
requirement:
|
19
|
+
requirement: !ruby/object:Gem::Requirement
|
20
20
|
none: false
|
21
21
|
requirements:
|
22
22
|
- - ! '>='
|
@@ -24,10 +24,15 @@ dependencies:
|
|
24
24
|
version: 1.2.9
|
25
25
|
type: :development
|
26
26
|
prerelease: false
|
27
|
-
version_requirements:
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.2.9
|
28
33
|
- !ruby/object:Gem::Dependency
|
29
34
|
name: rspec
|
30
|
-
requirement:
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
31
36
|
none: false
|
32
37
|
requirements:
|
33
38
|
- - ! '>='
|
@@ -35,7 +40,12 @@ dependencies:
|
|
35
40
|
version: 2.0.0
|
36
41
|
type: :development
|
37
42
|
prerelease: false
|
38
|
-
version_requirements:
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 2.0.0
|
39
49
|
description: GeoRuby provides geometric data types from the OGC 'Simple Features'
|
40
50
|
specification.
|
41
51
|
email: x@nofxx.com
|
@@ -66,6 +76,7 @@ files:
|
|
66
76
|
- lib/geo_ruby/simple_features/ewkb_parser.rb
|
67
77
|
- lib/geo_ruby/shp4r/shp.rb
|
68
78
|
- lib/geo_ruby/shp4r/dbf.rb
|
79
|
+
- lib/geo_ruby/kml.rb
|
69
80
|
- lib/geo_ruby/georss.rb
|
70
81
|
- spec/geo_ruby_spec.rb
|
71
82
|
- spec/data/point.dbf
|
@@ -92,6 +103,7 @@ files:
|
|
92
103
|
- spec/data/polygon.shx
|
93
104
|
- spec/data/polygon.shp
|
94
105
|
- spec/spec_helper.rb
|
106
|
+
- spec/geo_ruby/kml_spec.rb
|
95
107
|
- spec/geo_ruby/gpx4r/gpx_spec.rb
|
96
108
|
- spec/geo_ruby/geojson_spec.rb
|
97
109
|
- spec/geo_ruby/simple_features/multi_line_string_spec.rb
|
@@ -126,7 +138,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
126
138
|
version: '0'
|
127
139
|
segments:
|
128
140
|
- 0
|
129
|
-
hash:
|
141
|
+
hash: 3326906326237505079
|
130
142
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
143
|
none: false
|
132
144
|
requirements:
|
@@ -135,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
147
|
version: '0'
|
136
148
|
requirements: []
|
137
149
|
rubyforge_project:
|
138
|
-
rubygems_version: 1.8.
|
150
|
+
rubygems_version: 1.8.23
|
139
151
|
signing_key:
|
140
152
|
specification_version: 3
|
141
153
|
summary: Ruby data holder for OGC Simple Features
|