georuby 1.9.7 → 1.9.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|