rgeo 0.2.2 → 0.2.3

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.
@@ -40,93 +40,163 @@ module RGeo
40
40
 
41
41
 
42
42
  # This module contains an implementation of the CS (coordinate
43
- # systems) package of the OGC Coordinate Transform spec. It contains
43
+ # systems) package of the OGC Coordinate Transform spec. It provides
44
44
  # classes for representing ellipsoids, datums, coordinate systems,
45
45
  # and other related concepts, as well as a parser for the WKT format
46
46
  # for specifying coordinate systems.
47
47
  #
48
48
  # Generally, the easiest way to create coordinate system objects is
49
49
  # to use RGeo::CoordSys::CS.create_from_wkt, which parses the WKT
50
- # format. This and other methods of the FactoryMethods module are all
51
- # available as convenient module functions on the CS module.
50
+ # format. You can also use the create methods available for each
51
+ # object class.
52
52
  #
53
- # Almost the entire spec is implemented here. Currently missing are:
53
+ # Most but not all of the spec is implemented here.
54
+ # Currently missing are:
54
55
  #
55
56
  # * XML format is not implemented. We're assuming that WKT is the
56
57
  # preferred format.
57
58
  # * The PT and CT packages are not implemented.
58
59
  # * FittedCoordinateSystem is not implemented.
60
+ # * The defaultEnvelope attribute of CS_CoordinateSystem is not
61
+ # implemented.
59
62
 
60
63
  module CS
61
64
 
62
65
 
63
- module FactoryMethods
66
+ # A class implementing the CS_CoordinateSystemFactory interface.
67
+ # It provides methods for building up complex objects from simpler
68
+ # objects or values.
69
+ #
70
+ # Note that the methods of CS_CoordinateSystemFactory do not provide
71
+ # facilities for setting the authority. If you need to set authority
72
+ # values, use the create methods for the object classes themselves.
73
+
74
+ class CoordinateSystemFactory
75
+
76
+
77
+ # Create a CompoundCoordinateSystem from a name, and two
78
+ # constituent coordinate systems.
64
79
 
65
80
  def create_compound_coordinate_system(name_, head_, tail_)
66
81
  CompoundCoordinateSystem.create(name_, head_, tail_)
67
82
  end
68
83
 
84
+
85
+ # Create an Ellipsoid from a name, semi-major axis, and semi-minor
86
+ # axis. You can also provide a LinearUnit, but this is optional
87
+ # and may be set to nil.
88
+
69
89
  def create_ellipsoid(name_, semi_major_axis_, semi_minor_axis_, linear_unit_)
70
90
  Ellipsoid.create_ellipsoid(name_, semi_major_axis_, semi_minor_axis_, linear_unit_)
71
91
  end
72
92
 
93
+
94
+ # Create an Ellipsoid from a name, semi-major axis, and an inverse
95
+ # flattening factor. You can also provide a LinearUnit, but this
96
+ # is optional and may be set to nil.
97
+
73
98
  def create_flattened_sphere(name_, semi_major_axis_, inverse_flattening_, linear_unit_)
74
99
  Ellipsoid.create_flattened_sphere(name_, semi_major_axis_, inverse_flattening_, linear_unit_)
75
100
  end
76
101
 
102
+
103
+ # Create any object given the OGC WKT format. Raises
104
+ # Error::ParseError if a syntax error is encounterred.
105
+
77
106
  def create_from_wkt(str_)
78
107
  WKTParser.new(str_).parse
79
108
  end
80
109
 
110
+
111
+ # Create a GeographicCoordinateSystem, given a name, an
112
+ # AngularUnit, a HorizontalDatum, a PrimeMeridian, and two
113
+ # AxisInfo objects. The AxisInfo objects are optional and may be
114
+ # set to nil.
115
+
81
116
  def create_geographic_coordinate_system(name_, angular_unit_, horizontal_datum_, prime_meridian_, axis0_, axis1_)
82
117
  GeographicCoordinateSystem.create(name_, angular_unit_, horizontal_datum_, prime_meridian_, axis0_, axis1_)
83
118
  end
84
119
 
120
+
121
+ # Create a HorizontalDatum given a name, a horizontal datum type
122
+ # code, an Ellipsoid, and a WGS84ConversionInfo. The
123
+ # WGS84ConversionInfo is optional and may be set to nil.
124
+
85
125
  def create_horizontal_datum(name_, horizontal_datum_type_, ellipsoid_, to_wgs84_)
86
126
  HorizontalDatum.create(name_, horizontal_datum_type_, ellipsoid_, to_wgs84_)
87
127
  end
88
128
 
129
+
130
+ # Create a LocalCoordinateSystem given a name, a LocalDatum, a
131
+ # Unit, and an array of at least one AxisInfo.
132
+
89
133
  def create_local_coordinate_system(name_, datum_, unit_, axes_)
90
134
  LocalCoordinateSystem.create(name_, datum_, unit_, axes_)
91
135
  end
92
136
 
137
+
138
+ # Create a LocalDatum given a name and a local datum type code.
139
+
93
140
  def create_local_datum(name_, local_datum_type_)
94
141
  LocalDatum.create(name, local_datum_type_)
95
142
  end
96
143
 
144
+
145
+ # Create a PrimeMeridian given a name, an AngularUnit, and a
146
+ # longitude offset.
147
+
97
148
  def create_prime_meridian(name_, angular_unit_, longitude_)
98
149
  PrimeMeridian.create(name, angular_unit_, longitude_)
99
150
  end
100
151
 
152
+
153
+ # Create a ProjectedCoordinateSystem given a name, a
154
+ # GeographicCoordinateSystem, and Projection, a LinearUnit, and
155
+ # two AxisInfo objects. The AxisInfo objects are optional and may
156
+ # be set to nil.
157
+
101
158
  def create_projected_coordinate_system(name_, gcs_, projection_, linear_unit_, axis0_, axis1_)
102
159
  ProjectedCoordinateSystem.create(name_, gcs_, projection_, linear_unit_, axis0_, axis1_)
103
160
  end
104
161
 
162
+
163
+ # Create a Projection given a name, a projection class, and an
164
+ # array of ProjectionParameter.
165
+
105
166
  def create_projection(name_, wkt_projection_class_, parameters_)
106
167
  Projection.create(name_, wkt_projection_class_, parameters_)
107
168
  end
108
169
 
170
+
171
+ # Create a VerticalCoordinateSystem given a name, a VerticalDatum,
172
+ # a VerticalUnit, and an AxisInfo. The AxisInfo is optional and
173
+ # may be nil.
174
+
109
175
  def create_vertical_coordinate_system(name_, vertical_datum_, vertical_unit_, axis_)
110
176
  VerticalCoordinateSystem.create(name_, vertical_datum_, vertical_unit_, axis_)
111
177
  end
112
178
 
179
+
180
+ # Create a VerticalDatum given a name ane a datum type code.
181
+
113
182
  def create_vertical_datum(name_, vertical_datum_type_)
114
183
  VerticalDatum.create(name_, vertical_datum_type_)
115
184
  end
116
185
 
117
- end
118
-
119
-
120
- class CoordinateSystemFactory
121
-
122
- include FactoryMethods
123
186
 
124
187
  end
125
188
 
126
189
 
127
190
  class << self
128
191
 
129
- include FactoryMethods
192
+
193
+ # Parsees OGC WKT format and returns the object created. Raises
194
+ # Error::ParseError if a syntax error is encounterred.
195
+
196
+ def create_from_wkt(str_)
197
+ WKTParser.new(str_).parse
198
+ end
199
+
130
200
 
131
201
  end
132
202
 
@@ -225,6 +225,7 @@ module RGeo
225
225
  class TypeString < ::String # :nodoc:
226
226
  end
227
227
 
228
+
228
229
  class AuthorityClause # :nodoc:
229
230
 
230
231
  def initialize(name_, code_)
@@ -41,11 +41,84 @@ module RGeo
41
41
  module SRSDatabase
42
42
 
43
43
 
44
+ # A spatial reference database implementation that uses ActiveRecord
45
+ # to access a spatial reference table provided by a spatial database
46
+ # implementation. You can use this class to obtain coordinate system
47
+ # information from your installation of, e.g. PostGIS.
48
+
44
49
  class ActiveRecordTable
45
50
 
46
51
  @@class_counter = 0
47
52
 
48
53
 
54
+ # Create a new ActiveRecord-backed database connection.
55
+ #
56
+ # Options include:
57
+ #
58
+ # <tt>:ar_class</tt>::
59
+ # An ActiveRecord class to use. You may provide this if you
60
+ # already have an ActiveRecord class that accesses the table.
61
+ # If not provided, an ActiveRecord class will be generated
62
+ # for you, using the <tt>:ar_base_class</tt>,
63
+ # <tt>:database_config</tt>, and <tt>:table_name</tt> options.
64
+ # <tt>:ar_base_class</tt>::
65
+ # Specify an ActiveRecord base class to use when generating an
66
+ # ActiveRecord class. Default is ::ActiveRecord::Base. You may
67
+ # want to use this if you have a base class already that
68
+ # specifies an existing database connection and/or other
69
+ # class-scope options.
70
+ # <tt>:database_config</tt>::
71
+ # If provided, <tt>establish_connection</tt> will be called on
72
+ # the generated ActiveRecord class, with the given value.
73
+ # <tt>:table_name</tt>::
74
+ # The table name for the new ActiveRecord class. Defaults to
75
+ # the value <tt>spatial_ref_sys</tt>, which is the OGC-specified
76
+ # name for this table.
77
+ # <tt>:srid_column</tt>::
78
+ # The name of the SRID column. Defaults to "srid", which is the
79
+ # OGC-specified name for this column.
80
+ # <tt>:auth_name_column</tt>::
81
+ # The name of the authority name column. On an OGC-compliant
82
+ # database, this column should be named "auth_name". However,
83
+ # the default is set to nil; you should set this option
84
+ # explicitly if you want to read the authority name.
85
+ # <tt>:auth_srid_column</tt>::
86
+ # The name of the authority srid column. On an OGC-compliant
87
+ # database, this column should be named "auth_srid". However,
88
+ # the default is set to nil; you should set this option
89
+ # explicitly if you want to read the authority's srid.
90
+ # <tt>:name_column</tt>::
91
+ # The name of the coordinate system name column. This column is
92
+ # not part of the OGC spec, but it is included in some spatial
93
+ # database implementations. Default is nil.
94
+ # <tt>:description_column</tt>::
95
+ # The name of the coordinate system description column. This
96
+ # column is not part of the OGC spec, but may be included in
97
+ # some spatial database implementations. Default is nil.
98
+ # <tt>:srtext_column</tt>::
99
+ # The name of the spatial reference WKT column. On an
100
+ # OGC-compliant database, this column should be named "srtext".
101
+ # However, not all databases include this column, so the default
102
+ # is set to nil; you should set this option explicitly if you
103
+ # want to read the OGC coordinate system specification.
104
+ # <tt>:proj4text_column</tt>::
105
+ # The name of the Proj4 format projection spec column. This
106
+ # column is not part of the OGC spec, but may be included in
107
+ # some spatial database implementations. Default is nil.
108
+ # <tt>:cache</tt>::
109
+ # If set to true, entries are cached when first retrieved, so
110
+ # subsequent requests do not have to make a database round trip.
111
+ # Default is false.
112
+ #
113
+ # Some option settings may be provided by the ActiveRecord
114
+ # connection adapter, if the ActiveRecord class's connection uses
115
+ # an adapter that is RGeo-savvy. The "postgis" and "spatialite"
116
+ # adapters are such adapters. They automatically provide the
117
+ # <tt>:table_name</tt> and all the relevant column settings for
118
+ # the database-provided spatial reference table as defaults.
119
+ # However, you can still override those settings if you want to
120
+ # use a custom table.
121
+
49
122
  def initialize(opts_={})
50
123
  @cache = opts_[:cache] ? {} : nil
51
124
  @ar_class = opts_[:ar_class]
@@ -55,13 +128,18 @@ module RGeo
55
128
  self.class.const_set("Klass#{@@class_counter}", @ar_class)
56
129
  @@class_counter += 1
57
130
  @ar_class.class_eval do
58
- set_table_name(opts_[:table_name] || 'spatial_ref_sys')
131
+ establish_connection(opts_[:database_config]) if opts_[:database_config]
59
132
  end
60
133
  end
61
134
  connection_ = @ar_class.connection
62
135
  if connection_.respond_to?(:srs_database_columns)
63
136
  opts_ = connection_.srs_database_columns.merge(opts_)
64
137
  end
138
+ unless opts_[:ar_class]
139
+ @ar_class.class_eval do
140
+ set_table_name(opts_[:table_name] || 'spatial_ref_sys')
141
+ end
142
+ end
65
143
  @srid_column = opts_[:srid_column] || 'srid'
66
144
  @auth_name_column = opts_[:auth_name_column]
67
145
  @auth_srid_column = opts_[:auth_srid_column]
@@ -72,6 +150,8 @@ module RGeo
72
150
  end
73
151
 
74
152
 
153
+ # Retrieve an Entry given an integer SRID.
154
+
75
155
  def get(ident_)
76
156
  ident_ = ident_.to_i
77
157
  return @cache[ident_] if @cache && @cache.include?(ident_)
@@ -97,6 +177,8 @@ module RGeo
97
177
  end
98
178
 
99
179
 
180
+ # Clears the cache if a cache is active.
181
+
100
182
  def clear_cache
101
183
  @cache.clear if @cache
102
184
  end
@@ -51,14 +51,25 @@ module RGeo
51
51
  module SRSDatabase
52
52
 
53
53
 
54
+ # Interface specification for spatial reference system databases.
55
+ # This module exists primarily for the sake of documentation.
56
+ # Database implementations need not actually include this module,
57
+ # but at least need to duck-type its methods.
58
+
54
59
  module Interface
55
60
 
56
61
 
62
+ # Retrieve an Entry given an identifier. The identifier is usually
63
+ # a numeric spatial reference ID (SRID), but could be a string
64
+ # value for certain database types.
65
+
57
66
  def get(ident_)
58
67
  nil
59
68
  end
60
69
 
61
70
 
71
+ # Clears any cache utilized by this database.
72
+
62
73
  def clear_cache
63
74
  nil
64
75
  end
@@ -67,8 +78,35 @@ module RGeo
67
78
  end
68
79
 
69
80
 
81
+ # An entry in a spatial reference system database.
82
+ # Every entry has an identifier, but all the other attributes are
83
+ # optional and may or may not be present depending on the database.
84
+
70
85
  class Entry
71
86
 
87
+
88
+ # Create an entry.
89
+ # You must provide an identifier, which may be numeric or a
90
+ # string. The data hash should contain any other attributes,
91
+ # keyed by symbol.
92
+ #
93
+ # Some attribute inputs have special behaviors:
94
+ #
95
+ # <tt>:coord_sys</tt>::
96
+ # You can pass a CS coordinate system object, or a string in
97
+ # WKT format.
98
+ # <tt>:proj4</tt>::
99
+ # You can pass a Proj4 object, or a proj4-format string.
100
+ # <tt>:name</tt>::
101
+ # If the name is not provided directly, it is taken from the
102
+ # coord_sys.
103
+ # <tt>:authority</tt>::
104
+ # If the authority name is not provided directly, it is taken
105
+ # from the coord_sys.
106
+ # <tt>:authority_code</tt>::
107
+ # If the authority code is not provided directly, it is taken
108
+ # from the coord_sys.
109
+
72
110
  def initialize(ident_, data_={})
73
111
  @identifier = ident_
74
112
  @authority = data_[:authority]
@@ -94,14 +132,29 @@ module RGeo
94
132
  end
95
133
  end
96
134
 
135
+
136
+ # The database key or identifier.
97
137
  attr_reader :identifier
138
+
139
+ # The authority name, if present. Example: "epsg".
98
140
  attr_reader :authority
141
+
142
+ # The authority code, e.g. an EPSG code.
99
143
  attr_reader :authority_code
144
+
145
+ # A human-readable name for this coordinate system.
100
146
  attr_reader :name
147
+
148
+ # A human-readable description for this coordinate system.
101
149
  attr_reader :description
150
+
151
+ # The CS::CoordinateSystem object.
102
152
  attr_reader :coord_sys
153
+
154
+ # The Proj4 object.
103
155
  attr_reader :proj4
104
156
 
157
+
105
158
  end
106
159
 
107
160
 
@@ -41,10 +41,44 @@ module RGeo
41
41
  module SRSDatabase
42
42
 
43
43
 
44
+ # A spatial reference database implementation backed by coordinate
45
+ # system files installed as part of the proj4 library. For a given
46
+ # Proj4Data object, you specify a single file (e.g. the epsg data
47
+ # file), and you can retrieve records by ID number.
48
+
44
49
  class Proj4Data
45
50
 
46
51
 
47
- def initialize(path_, opts_={})
52
+ # Connect to one of the proj4 data files. You should provide the
53
+ # file name, optionally the installation directory if it is not
54
+ # in a typical location, and several additional options.
55
+ #
56
+ # These options are recognized:
57
+ #
58
+ # <tt>:dir</tt>::
59
+ # The path for the share/proj directory that contains the
60
+ # requested data file. By default, the Proj4Data class will
61
+ # try a number of directories for you, including
62
+ # /usr/local/share/proj, /opt/local/share/proj, /usr/share/proj,
63
+ # and a few other variants. However, if you have proj4 installed
64
+ # elsewhere, you can provide an explicit directory using this
65
+ # option. You may also pass nil as the value, in which case all
66
+ # the normal lookup paths will be disabled, and you will have to
67
+ # provide the full path as the file name.
68
+ # <tt>:cache</tt>::
69
+ # If set to true, this class caches previously looked up entries
70
+ # so subsequent lookups do not have to reread the file. If set
71
+ # to <tt>:read_all</tt>, then ALL values in the file are read in
72
+ # and cached the first time a lookup is done. If set to
73
+ # <tt>:preload</tt>, then ALL values in the file are read in
74
+ # immediately when the database is created. Default is false,
75
+ # indicating that the file will be reread on every lookup.
76
+ # <tt>:authority</tt>::
77
+ # If set, its value is taken as the authority name for all
78
+ # entries. The authority code will be set to the identifier. If
79
+ # not set, then the authority fields of entries will be blank.
80
+
81
+ def initialize(filename_, opts_={})
48
82
  dir_ = nil
49
83
  if opts_.include?(:dir)
50
84
  dir_ = opts_[:dir]
@@ -56,36 +90,47 @@ module RGeo
56
90
  end
57
91
  end
58
92
  end
59
- @path = dir_ ? "#{dir_}/#{path_}" : path_
60
- @cache = opts_[:cache] ? {} : nil
93
+ @path = dir_ ? "#{dir_}/#{filename_}" : filename_
61
94
  @authority = opts_[:authority]
62
- @populate_state = @cache && opts_[:read_all] ? 1 : 0
95
+ if opts_[:cache]
96
+ @cache = {}
97
+ case opts_[:cache]
98
+ when :read_all
99
+ @populate_state = 1
100
+ when :preload
101
+ _search_file(nil)
102
+ @populate_state = 2
103
+ else
104
+ @populate_state = 0
105
+ end
106
+ else
107
+ @cache = nil
108
+ @populate_state = 0
109
+ end
63
110
  end
64
111
 
65
112
 
113
+ # Retrieve the Entry for the given ID number.
114
+
66
115
  def get(ident_)
67
116
  ident_ = ident_.to_s
68
117
  return @cache[ident_] if @cache && @cache.include?(ident_)
69
118
  result_ = nil
70
119
  if @populate_state == 0
71
120
  data_ = _search_file(ident_)
72
- unless data_
73
- @cache[ident_] = nil if @cache
74
- return nil
75
- end
76
- result_ = Entry.new(ident_, :authority => @authority, :authority_code => @authority ? ident_ : nil, :name => data_[1], :proj4 => data_[2])
121
+ result_ = Entry.new(ident_, :authority => @authority, :authority_code => @authority ? ident_ : nil, :name => data_[1], :proj4 => data_[2]) if data_
77
122
  @cache[ident_] = result_ if @cache
78
123
  elsif @populate_state == 1
79
- _search_file(nil) do |id_, name_, text_|
80
- @cache[id_] = Entry.new(id_, :authority => @authority, :authority_code => @authority ? id_ : nil, :name => name_, :proj4 => text_)
81
- result_ = @cache[id_] if id_ == ident_
82
- end
124
+ _search_file(nil)
125
+ result_ = @cache[ident_]
83
126
  @populate_state = 2
84
127
  end
85
128
  result_
86
129
  end
87
130
 
88
131
 
132
+ # Clear the cache if one exists.
133
+
89
134
  def clear_cache
90
135
  @cache.clear if @cache
91
136
  @populate_state = 1 if @populate_state == 2
@@ -114,8 +159,8 @@ module RGeo
114
159
  if line_[-2..-1] == '<>'
115
160
  cur_text_ << line_[0..-3].strip
116
161
  cur_text_ = cur_text_.join(' ')
117
- if block_given?
118
- yield(ident_, cur_name_, cur_text_)
162
+ if ident_.nil?
163
+ @cache[ident_] = Entry.new(ident_, :authority => @authority, :authority_code => @authority ? id_ : nil, :name => cur_name_, :proj4 => cur_text_)
119
164
  end
120
165
  if cur_ident_ == ident_
121
166
  return [ident_, cur_name_, cur_text_]