nofxx-georuby 1.3.9 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +48 -13
- data/VERSION +1 -0
- data/georuby.gemspec +29 -17
- data/lib/geo_ruby.rb +4 -1
- data/lib/geo_ruby/gpx.rb +1 -0
- data/lib/geo_ruby/gpx4r/gpx.rb +109 -0
- data/lib/geo_ruby/shp.rb +1 -0
- data/lib/geo_ruby/shp4r/dbf.rb +21 -164
- data/lib/geo_ruby/shp4r/shp.rb +1 -5
- data/spec/data/gpx/long.gpx +1642 -0
- data/spec/data/gpx/long.kml +31590 -0
- data/spec/data/gpx/long.nmea +2220 -0
- data/spec/data/gpx/short.gpx +13634 -0
- data/spec/data/gpx/short.kml +130 -0
- data/spec/geo_ruby/gpx4r/gpx_spec.rb +50 -0
- data/spec/geo_ruby/shp4r/shp_spec.rb +3 -1
- data/spec/geo_ruby/simple_features/ewkt_parser_spec.rb +1 -1
- data/spec/geo_ruby/simple_features/geometry_spec.rb +1 -1
- data/spec/geo_ruby/simple_features/georss_parser_spec.rb +1 -1
- data/spec/geo_ruby/simple_features/multi_polygon_spec.rb +1 -1
- data/spec/geo_ruby/simple_features/point_spec.rb +1 -1
- data/spec/spec_helper.rb +3 -0
- metadata +27 -16
- data/VERSION.yml +0 -4
data/README.txt
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
=GeoRuby
|
1
|
+
= GeoRuby
|
2
2
|
|
3
3
|
This is a fork of GeoRuby. It is intended as a holder for data returned from PostGIS and the Spatial Extensions of MySql. The data model roughly follows the OGC "Simple Features for SQL" specification (see http://www.opengis.org/docs/99-049.pdf), although without any kind of advanced functionalities (such as geometric operators or reprojections). It also supports various output and input formats (GeoRSS, KML, Shapefile).
|
4
4
|
|
5
|
-
|
5
|
+
|
6
|
+
=== Available data types
|
7
|
+
|
6
8
|
The following geometric data types are provided :
|
7
9
|
- Point
|
8
10
|
- Line string
|
@@ -17,17 +19,24 @@ They can be in 2D, 3DZ, 3DM, and 4D.
|
|
17
19
|
|
18
20
|
On top of this an Envelope class is available, to contain the bounding box of a geometry.
|
19
21
|
|
20
|
-
|
22
|
+
|
23
|
+
=== Input and output
|
24
|
+
|
21
25
|
These geometries can be input and output in WKB/EWKB/WKT/EWKT format (as well as the related HexWKB and HexEWKB formats). HexEWKB and WKB are the default form under which geometric data is returned respectively from PostGIS and MySql.
|
22
26
|
|
23
27
|
GeoRSS Simple, GeoRSS W3CGeo, GeoRSS GML can also be input and output. Note that they will not output valid RSS, but just the part strictly concerning the geometry as outlined in http://www.georss.org/1/ . Since the model does not allow multiple geometries, for geometry collections, only the first geometry will be output. Similarly, for polygons, the GeoRSS output will only contain the outer ring. As for W3CGeo output, only points can be output, so the first point of the geometry is chosen. By default the Simple format is output. Envelope can also be output in these formats: The box geometric type is chosen (except for W3CGeo, where the center of the envelope is chose). These formats can also be input and a GeoRuby geometry will be created. Note that it will not read a valid RSS file, only a geometry string.
|
24
28
|
|
25
29
|
On top of that, there is now support for KML output and input. As for GeoRSS, a valid KML file will not be output, but only the geometric data. Options <tt>:id</tt>, <tt>:extrude</tt>, <tt>:tesselate</tt> and <tt>:altitude_mode</tt> can be given. Note that if the <tt>:altitude_mode</tt> option is not passed or set to <tt>clampToGround</tt>, the altitude data will not be output even if present. Envelopes output a LatLonAltBox instead of a geometry. For the output, the following geometric types are supported : Point, LineString, Polygon.
|
26
30
|
|
27
|
-
|
31
|
+
|
32
|
+
=== SHP reading et writing
|
33
|
+
|
28
34
|
Georuby has support for reading ESRI shapefiles (http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf). A tool called <tt>shp2sql.rb</tt> is also provided : it shows how to use the SHP reading functionality together with the spatial adapter plugin for Rails to import spatial features into MySQL and PostGIS.
|
29
35
|
|
30
36
|
Here is an example of Shapefile reading, that goes through all the geometries in a file and disaply the values of the attributes :
|
37
|
+
|
38
|
+
require 'geo_ruby/shp'
|
39
|
+
|
31
40
|
ShpFile.open(shpfile) do |shp|
|
32
41
|
shp.each do |shape|
|
33
42
|
geom = shape.geometry #a GeoRuby SimpleFeature
|
@@ -41,10 +50,13 @@ Here is an example of Shapefile reading, that goes through all the geometries in
|
|
41
50
|
Support for ESRI shapefile creation and modification has been added as well. New shapefiles can be created given a geometry type and specifications for the DBF fields. Data can be added and removed from an existing shapefile. An update operation is also provided for convenience : it just performs a 'delete' and an 'add', which means the index of the modified record will change. Note that once a shapefile has been created, GeoRuby does not allow the modification of the schema (it will probably be done in a subsequent version).
|
42
51
|
|
43
52
|
Here is an example of how to create a new Shapefile with 2 fields :
|
53
|
+
|
44
54
|
shpfile = ShpFile.create('hello.shp',ShpType::POINT,[Dbf::Field.new("Hoyoyo","C",10),Dbf::Field.new("Boyoul","N",10,0)])
|
55
|
+
|
45
56
|
The file is then open for reading and writing.
|
46
57
|
|
47
58
|
Here is an example of how to write to a shapefile (created or not with GeoRuby) :
|
59
|
+
|
48
60
|
shpfile = ShpFile.open('places.shp')
|
49
61
|
shpfile.transaction do |tr|
|
50
62
|
tr.add(ShpRecord.new(Point.from_x_y(123.4,123.4),'Hoyoyo' => "AEZ",'Bouyoul' => 45))
|
@@ -57,27 +69,50 @@ Note the transaction is just there so the operations on the files can be buffere
|
|
57
69
|
|
58
70
|
Also currently, error reporting is minimal and it has not been tested that thoroughly so caveat emptor and backup before performing any destructive operation.
|
59
71
|
|
60
|
-
|
72
|
+
|
73
|
+
=== GPX Reading
|
74
|
+
|
75
|
+
You can read and convert GPX Files to LineString/Polygon:
|
76
|
+
|
77
|
+
gpxfile = GpxFile.open(tour.gpx')
|
78
|
+
gpxfile.as_line_string
|
79
|
+
=> GeoRuby::SimpleFeatures::LineString..
|
80
|
+
|
81
|
+
|
82
|
+
=== Installation
|
83
|
+
|
61
84
|
To install the latest version, just type :
|
62
|
-
gem install GeoRuby
|
63
85
|
|
64
|
-
|
86
|
+
gem install nofxx-georuby
|
87
|
+
|
88
|
+
|
89
|
+
=== Changes since the last version
|
90
|
+
|
65
91
|
- Writing of ESRI shapefiles
|
66
92
|
- Reading of ESRI shapefiles
|
67
93
|
- Addition of a small tool to import spatial features in MySQL and PostGIS from a SHP file
|
68
94
|
|
69
|
-
|
95
|
+
|
96
|
+
=== Coming in the next versions
|
97
|
+
|
70
98
|
- Schema modification of existing shapefiles
|
71
99
|
- More error reporting when writing shapefiles
|
72
100
|
- More tests on writing shapefiles ; tests on real-world shapefiles
|
73
101
|
- Better shp2sql import tool
|
74
102
|
- Documentation
|
75
103
|
|
76
|
-
===Acknowledgement
|
77
|
-
The SHP reading part uses a modified version of the DBF library (http://rubyforge.org/projects/dbf/) by Keith Morrison (http://infused.org). Thanks also to Pramukta Kumar and Pete Schwamb for their contributions.
|
78
104
|
|
79
|
-
===
|
105
|
+
=== Acknowledgement
|
106
|
+
|
107
|
+
The SHP reading part uses the DBF library (http://rubyforge.org/projects/dbf/) by Keith Morrison (http://infused.org).
|
108
|
+
Thanks also to Pramukta Kumar and Pete Schwamb for their contributions.
|
109
|
+
|
110
|
+
|
111
|
+
=== License
|
112
|
+
|
80
113
|
GeoRuby is released under the MIT license.
|
81
114
|
|
82
|
-
===Support
|
83
|
-
|
115
|
+
=== Support
|
116
|
+
|
117
|
+
Any questions, enhancement proposals, bug notifications or corrections
|
118
|
+
can be sent to mailto:guilhem.vellut@gmail.com.
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.6.0
|
data/georuby.gemspec
CHANGED
@@ -1,12 +1,15 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
1
4
|
# -*- encoding: utf-8 -*-
|
2
5
|
|
3
6
|
Gem::Specification.new do |s|
|
4
7
|
s.name = %q{georuby}
|
5
|
-
s.version = "1.
|
8
|
+
s.version = "1.6.0"
|
6
9
|
|
7
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
11
|
s.authors = ["Guilhem Vellut", "Marcos Augusto"]
|
9
|
-
s.date = %q{2009-
|
12
|
+
s.date = %q{2009-08-16}
|
10
13
|
s.description = %q{GeoRuby provides geometric data types from the OGC 'Simple Features' specification.}
|
11
14
|
s.email = %q{x@nofxx.com}
|
12
15
|
s.extra_rdoc_files = [
|
@@ -19,9 +22,12 @@ Gem::Specification.new do |s|
|
|
19
22
|
"LICENSE",
|
20
23
|
"README.txt",
|
21
24
|
"Rakefile",
|
22
|
-
"VERSION
|
25
|
+
"VERSION",
|
23
26
|
"georuby.gemspec",
|
24
27
|
"lib/geo_ruby.rb",
|
28
|
+
"lib/geo_ruby/gpx.rb",
|
29
|
+
"lib/geo_ruby/gpx4r/gpx.rb",
|
30
|
+
"lib/geo_ruby/shp.rb",
|
25
31
|
"lib/geo_ruby/shp4r/dbf.rb",
|
26
32
|
"lib/geo_ruby/shp4r/shp.rb",
|
27
33
|
"lib/geo_ruby/simple_features/envelope.rb",
|
@@ -43,6 +49,11 @@ Gem::Specification.new do |s|
|
|
43
49
|
"script/destroy",
|
44
50
|
"script/generate",
|
45
51
|
"script/txt2html",
|
52
|
+
"spec/data/gpx/long.gpx",
|
53
|
+
"spec/data/gpx/long.kml",
|
54
|
+
"spec/data/gpx/long.nmea",
|
55
|
+
"spec/data/gpx/short.gpx",
|
56
|
+
"spec/data/gpx/short.kml",
|
46
57
|
"spec/data/multipoint.dbf",
|
47
58
|
"spec/data/multipoint.shp",
|
48
59
|
"spec/data/multipoint.shx",
|
@@ -55,6 +66,7 @@ Gem::Specification.new do |s|
|
|
55
66
|
"spec/data/polyline.dbf",
|
56
67
|
"spec/data/polyline.shp",
|
57
68
|
"spec/data/polyline.shx",
|
69
|
+
"spec/geo_ruby/gpx4r/gpx_spec.rb",
|
58
70
|
"spec/geo_ruby/shp4r/shp_spec.rb",
|
59
71
|
"spec/geo_ruby/simple_features/envelope_spec.rb",
|
60
72
|
"spec/geo_ruby/simple_features/ewkb_parser_spec.rb",
|
@@ -74,30 +86,30 @@ Gem::Specification.new do |s|
|
|
74
86
|
"spec/spec.opts",
|
75
87
|
"spec/spec_helper.rb"
|
76
88
|
]
|
77
|
-
s.has_rdoc = true
|
78
89
|
s.homepage = %q{http://github.com/nofxx/georuby}
|
79
90
|
s.rdoc_options = ["--charset=UTF-8"]
|
80
91
|
s.require_paths = ["lib"]
|
81
|
-
s.rubygems_version = %q{1.3.
|
92
|
+
s.rubygems_version = %q{1.3.5}
|
82
93
|
s.summary = %q{Ruby data holder for OGC Simple Features}
|
83
94
|
s.test_files = [
|
84
|
-
"spec/
|
85
|
-
"spec/
|
86
|
-
"spec/geo_ruby/
|
95
|
+
"spec/geo_ruby_spec.rb",
|
96
|
+
"spec/spec_helper.rb",
|
97
|
+
"spec/geo_ruby/gpx4r/gpx_spec.rb",
|
98
|
+
"spec/geo_ruby/shp4r/shp_spec.rb",
|
99
|
+
"spec/geo_ruby/simple_features/point_spec.rb",
|
87
100
|
"spec/geo_ruby/simple_features/geometry_factory_spec.rb",
|
88
|
-
"spec/geo_ruby/simple_features/
|
89
|
-
"spec/geo_ruby/simple_features/
|
90
|
-
"spec/geo_ruby/simple_features/multi_point_spec.rb",
|
91
|
-
"spec/geo_ruby/simple_features/ewkt_parser_spec.rb",
|
101
|
+
"spec/geo_ruby/simple_features/envelope_spec.rb",
|
102
|
+
"spec/geo_ruby/simple_features/polygon_spec.rb",
|
92
103
|
"spec/geo_ruby/simple_features/line_string_spec.rb",
|
93
104
|
"spec/geo_ruby/simple_features/multi_line_string_spec.rb",
|
94
|
-
"spec/geo_ruby/simple_features/
|
105
|
+
"spec/geo_ruby/simple_features/ewkt_parser_spec.rb",
|
95
106
|
"spec/geo_ruby/simple_features/ewkb_parser_spec.rb",
|
107
|
+
"spec/geo_ruby/simple_features/linear_ring_spec.rb",
|
108
|
+
"spec/geo_ruby/simple_features/geometry_collection_spec.rb",
|
96
109
|
"spec/geo_ruby/simple_features/multi_polygon_spec.rb",
|
97
|
-
"spec/geo_ruby/simple_features/
|
98
|
-
"spec/geo_ruby/simple_features/
|
99
|
-
"spec/
|
100
|
-
"spec/spec_helper.rb"
|
110
|
+
"spec/geo_ruby/simple_features/multi_point_spec.rb",
|
111
|
+
"spec/geo_ruby/simple_features/georss_parser_spec.rb",
|
112
|
+
"spec/geo_ruby/simple_features/geometry_spec.rb"
|
101
113
|
]
|
102
114
|
|
103
115
|
if s.respond_to? :specification_version then
|
data/lib/geo_ruby.rb
CHANGED
@@ -16,4 +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
|
-
|
19
|
+
|
20
|
+
# Include if you need
|
21
|
+
# require 'geo_ruby/shp4r/shp'
|
22
|
+
# require 'geo_ruby/gpx4r/gpx'
|
data/lib/geo_ruby/gpx.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'geo_ruby/gpx4r/gpx'
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "nokogiri"
|
3
|
+
|
4
|
+
module GeoRuby
|
5
|
+
module Gpx4r
|
6
|
+
|
7
|
+
#An interface to GPX files
|
8
|
+
class GpxFile
|
9
|
+
attr_reader :record_count, :file_root #:xmin, :ymin, :xmax, :ymax, :zmin, :zmax, :mmin, :mmax, :file_length
|
10
|
+
|
11
|
+
include Enumerable
|
12
|
+
|
13
|
+
# Opens a GPX file. Both "abc.shp" and "abc" are accepted.
|
14
|
+
def initialize(file, with_z = true, with_m = true)
|
15
|
+
@file_root = file.gsub(/\.gpx$/i,"")
|
16
|
+
raise MalformedGpxException.new("Missing GPX File") unless
|
17
|
+
File.exists? @file_root + ".gpx"
|
18
|
+
@points, @envelope = [], nil
|
19
|
+
@gpx = File.open(@file_root + ".gpx", "rb")
|
20
|
+
parse_file
|
21
|
+
end
|
22
|
+
|
23
|
+
#force the reopening of the files compsing the shp. Close before calling this.
|
24
|
+
def reload!
|
25
|
+
initialize(@file_root)
|
26
|
+
end
|
27
|
+
|
28
|
+
#opens a GPX "file". If a block is given, the GpxFile object is yielded to it and is closed upon return. Else a call to <tt>open</tt> is equivalent to <tt>GpxFile.new(...)</tt>.
|
29
|
+
def self.open(file)
|
30
|
+
gpxfile = GpxFile.new(file)
|
31
|
+
if block_given?
|
32
|
+
yield gpxfile
|
33
|
+
gpxfile.close
|
34
|
+
else
|
35
|
+
gpxfile
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
#Closes a gpxfile
|
40
|
+
def close
|
41
|
+
@gpx.close
|
42
|
+
end
|
43
|
+
|
44
|
+
#Tests if the file has no record
|
45
|
+
def empty?
|
46
|
+
record_count == 0
|
47
|
+
end
|
48
|
+
|
49
|
+
#Goes through each record
|
50
|
+
def each
|
51
|
+
(0...record_count).each do |i|
|
52
|
+
yield get_record(i)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
alias :each_record :each
|
56
|
+
|
57
|
+
#Returns record +i+
|
58
|
+
def [](i)
|
59
|
+
get_record(i)
|
60
|
+
end
|
61
|
+
|
62
|
+
#Returns all the records
|
63
|
+
def records
|
64
|
+
@points
|
65
|
+
end
|
66
|
+
|
67
|
+
# Return the GPX file as LineString
|
68
|
+
def as_line_string
|
69
|
+
LineString.from_points(@points)
|
70
|
+
end
|
71
|
+
alias :as_polyline :as_line_string
|
72
|
+
|
73
|
+
# Return the GPX file as a Polygon
|
74
|
+
# If the GPX isn't closed, a line from the first
|
75
|
+
# to the last point will be created to close it.
|
76
|
+
def as_polygon
|
77
|
+
Polygon.from_points([@points << @points[-1]])
|
78
|
+
end
|
79
|
+
|
80
|
+
# Return GPX Envelope
|
81
|
+
def envelope
|
82
|
+
@envelope ||= as_polygon.envelope
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def get_record(i)
|
88
|
+
@points[i]
|
89
|
+
end
|
90
|
+
|
91
|
+
def parse_file
|
92
|
+
Nokogiri.HTML(@gpx.read).search("//trkpt").each do |tp|
|
93
|
+
@points << Point.from_x_y_z_m(tp["lon"].to_f, tp["lat"].to_f, tp.at("ele").inner_text.to_i, tp.at("time").inner_text)
|
94
|
+
end
|
95
|
+
close
|
96
|
+
@record_count = @points.length
|
97
|
+
rescue => e
|
98
|
+
raise MalformedGpxException.new("Bad GPX. Error: #{e}")
|
99
|
+
# trackpoint.at("gpxdata:hr").nil? # heartrate
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
class MalformedGpxException < StandardError
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
data/lib/geo_ruby/shp.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'geo_ruby/shp4r/shp'
|
data/lib/geo_ruby/shp4r/dbf.rb
CHANGED
@@ -1,180 +1,37 @@
|
|
1
|
-
# Copyright 2006 Keith Morrison (http://infused.org)
|
2
|
-
# Modified
|
1
|
+
# Uses the dbf lib, Copyright 2006 Keith Morrison (http://infused.org)
|
2
|
+
# Modified to work as external gem now
|
3
|
+
require 'rubygems'
|
4
|
+
require 'dbf'
|
3
5
|
|
4
6
|
module GeoRuby
|
5
7
|
module Shp4r
|
6
|
-
|
7
|
-
|
8
|
-
DBF_HEADER_SIZE = 32
|
9
|
-
DATE_REGEXP = /([\d]{4})([\d]{2})([\d]{2})/
|
10
|
-
VERSION_DESCRIPTIONS = {
|
11
|
-
"02" => "FoxBase",
|
12
|
-
"03" => "dBase III without memo file",
|
13
|
-
"04" => "dBase IV without memo file",
|
14
|
-
"05" => "dBase V without memo file",
|
15
|
-
"30" => "Visual FoxPro",
|
16
|
-
"31" => "Visual FoxPro with AutoIncrement field",
|
17
|
-
"7b" => "dBase IV with memo file",
|
18
|
-
"83" => "dBase III with memo file",
|
19
|
-
"8b" => "dBase IV with memo file",
|
20
|
-
"8e" => "dBase IV with SQL table",
|
21
|
-
"f5" => "FoxPro with memo file",
|
22
|
-
"fb" => "FoxPro without memo file"
|
23
|
-
}
|
24
|
-
|
25
|
-
class DBFError < StandardError; end
|
26
|
-
class UnpackError < DBFError; end
|
27
|
-
|
28
|
-
class Reader
|
29
|
-
attr_reader :field_count
|
30
|
-
attr_reader :fields
|
31
|
-
attr_reader :record_count
|
32
|
-
attr_reader :version
|
33
|
-
attr_reader :last_updated
|
34
|
-
attr_reader :header_length
|
35
|
-
attr_reader :record_length
|
36
|
-
|
37
|
-
def initialize(file)
|
38
|
-
@data_file = File.open(file, 'rb')
|
39
|
-
reload!
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.open(file)
|
43
|
-
reader = Reader.new(file)
|
44
|
-
if block_given?
|
45
|
-
yield reader
|
46
|
-
reader.close
|
47
|
-
else
|
48
|
-
reader
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def close
|
53
|
-
@data_file.close
|
54
|
-
end
|
55
|
-
|
56
|
-
def reload!
|
57
|
-
get_header_info
|
58
|
-
get_field_descriptors
|
59
|
-
end
|
60
|
-
|
61
|
-
def field(field_name)
|
62
|
-
@fields.detect {|f| f.name == field_name.to_s}
|
63
|
-
end
|
64
|
-
|
65
|
-
# An array of all the records contained in the database file
|
66
|
-
def records
|
67
|
-
seek_to_record(0)
|
68
|
-
@records ||= Array.new(@record_count) do |i|
|
69
|
-
if active_record?
|
70
|
-
build_record
|
71
|
-
else
|
72
|
-
seek_to_record(i + 1)
|
73
|
-
nil
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
alias_method :rows, :records
|
78
|
-
|
79
|
-
# Jump to record
|
80
|
-
def record(index)
|
81
|
-
seek_to_record(index)
|
82
|
-
active_record? ? build_record : nil
|
83
|
-
end
|
84
|
-
|
85
|
-
alias_method :row, :record
|
86
|
-
|
87
|
-
def version_description
|
88
|
-
VERSION_DESCRIPTIONS[version]
|
89
|
-
end
|
90
|
-
|
91
|
-
private
|
92
|
-
|
93
|
-
def active_record?
|
94
|
-
@data_file.read(1).unpack('H2').to_s == '20' rescue false
|
95
|
-
end
|
8
|
+
Dbf = DBF
|
96
9
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
when 'N'
|
102
|
-
record[field.name] = unpack_integer(field) rescue nil
|
103
|
-
when 'F'
|
104
|
-
record[field.name] = unpack_float(field) rescue nil
|
105
|
-
when 'D'
|
106
|
-
raw = unpack_string(field).to_s.strip
|
107
|
-
unless raw.empty?
|
108
|
-
begin
|
109
|
-
record[field.name] = Time.gm(*raw.match(DATE_REGEXP).to_a.slice(1,3).map {|n| n.to_i})
|
110
|
-
rescue
|
111
|
-
record[field.name] = Date.new(*raw.match(DATE_REGEXP).to_a.slice(1,3).map {|n| n.to_i}) rescue nil
|
112
|
-
end
|
113
|
-
end
|
114
|
-
when 'L'
|
115
|
-
record[field.name] = unpack_string(field) =~ /^(y|t)$/i ? true : false rescue false
|
116
|
-
when 'C'
|
117
|
-
record[field.name] = unpack_string(field).strip
|
118
|
-
else
|
119
|
-
record[field.name] = unpack_string(field)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
record
|
123
|
-
end
|
124
|
-
|
125
|
-
def get_header_info
|
126
|
-
@data_file.rewind
|
127
|
-
@version, @record_count, @header_length, @record_length = @data_file.read(DBF_HEADER_SIZE).unpack('H2xxxVvv')
|
128
|
-
@field_count = (@header_length - DBF_HEADER_SIZE + 1) / DBF_HEADER_SIZE
|
129
|
-
end
|
130
|
-
|
131
|
-
def get_field_descriptors
|
132
|
-
@fields = Array.new(@field_count) {|i| Field.new(*@data_file.read(32).unpack('a10xax4CC'))}
|
133
|
-
end
|
134
|
-
|
135
|
-
def seek(offset)
|
136
|
-
@data_file.seek(@header_length + offset)
|
137
|
-
end
|
138
|
-
|
139
|
-
def seek_to_record(index)
|
140
|
-
seek(@record_length * index)
|
141
|
-
end
|
142
|
-
|
143
|
-
def unpack_field(field)
|
144
|
-
@data_file.read(field.length).unpack("a#{field.length}")
|
145
|
-
end
|
146
|
-
|
147
|
-
def unpack_string(field)
|
148
|
-
unpack_field(field).to_s
|
149
|
-
end
|
150
|
-
|
151
|
-
def unpack_integer(field)
|
152
|
-
unpack_string(field).to_i
|
10
|
+
module Dbf
|
11
|
+
class Record
|
12
|
+
def [](v)
|
13
|
+
attributes[v.downcase]
|
153
14
|
end
|
15
|
+
end
|
154
16
|
|
155
|
-
|
156
|
-
|
17
|
+
class Field < Column
|
18
|
+
def initialize(name, type, length, decimal = 0)
|
19
|
+
super(name, type, length, decimal)
|
157
20
|
end
|
158
|
-
|
159
21
|
end
|
160
22
|
|
161
|
-
class
|
162
|
-
|
163
|
-
|
164
|
-
|
23
|
+
class Reader < Table
|
24
|
+
alias_method :fields, :columns
|
25
|
+
def header_length
|
26
|
+
@columns_count
|
27
|
+
end
|
165
28
|
|
166
|
-
def
|
167
|
-
|
168
|
-
if type == 'N' and decimal != 0
|
169
|
-
type = 'F'
|
170
|
-
end
|
171
|
-
@name, @type, @length, @decimal = name.strip, type,length, decimal
|
29
|
+
def self.open(f)
|
30
|
+
new(f)
|
172
31
|
end
|
173
|
-
end
|
174
32
|
|
175
|
-
|
33
|
+
def close(); nil; end
|
176
34
|
end
|
177
|
-
|
178
35
|
end
|
179
36
|
end
|
180
37
|
end
|