rgeo 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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_]