proj4rb 3.0.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog +26 -15
  3. data/README.rdoc +82 -44
  4. data/Rakefile +27 -27
  5. data/lib/api/api.rb +96 -118
  6. data/lib/api/api_5_0.rb +331 -300
  7. data/lib/api/api_5_1.rb +6 -6
  8. data/lib/api/api_5_2.rb +4 -4
  9. data/lib/api/api_6_0.rb +116 -14
  10. data/lib/api/api_6_1.rb +4 -4
  11. data/lib/api/api_6_2.rb +9 -6
  12. data/lib/api/api_6_3.rb +6 -0
  13. data/lib/api/api_7_0.rb +68 -0
  14. data/lib/api/api_7_1.rb +73 -0
  15. data/lib/api/api_7_2.rb +14 -0
  16. data/lib/api/api_8_0.rb +6 -0
  17. data/lib/api/api_8_1.rb +24 -0
  18. data/lib/api/api_8_2.rb +6 -0
  19. data/lib/api/api_9_1.rb +7 -0
  20. data/lib/api/api_9_2.rb +9 -0
  21. data/lib/api/api_experimental.rb +196 -0
  22. data/lib/proj/area.rb +73 -32
  23. data/lib/proj/axis_info.rb +44 -0
  24. data/lib/proj/bounds.rb +13 -0
  25. data/lib/proj/context.rb +174 -28
  26. data/lib/proj/conversion.rb +92 -0
  27. data/lib/proj/coordinate.rb +281 -197
  28. data/lib/proj/coordinate_operation_mixin.rb +381 -0
  29. data/lib/proj/coordinate_system.rb +137 -0
  30. data/lib/proj/crs.rb +672 -204
  31. data/lib/proj/crs_info.rb +47 -0
  32. data/lib/proj/database.rb +305 -0
  33. data/lib/proj/datum.rb +32 -0
  34. data/lib/proj/datum_ensemble.rb +34 -0
  35. data/lib/proj/ellipsoid.rb +77 -41
  36. data/lib/proj/error.rb +62 -9
  37. data/lib/proj/file_api.rb +166 -0
  38. data/lib/proj/grid.rb +121 -0
  39. data/lib/proj/grid_cache.rb +64 -0
  40. data/lib/proj/grid_info.rb +19 -0
  41. data/lib/proj/network_api.rb +92 -0
  42. data/lib/proj/operation.rb +42 -42
  43. data/lib/proj/operation_factory_context.rb +136 -0
  44. data/lib/proj/parameter.rb +38 -0
  45. data/lib/proj/parameters.rb +106 -0
  46. data/lib/proj/pj_object.rb +670 -80
  47. data/lib/proj/pj_objects.rb +44 -0
  48. data/lib/proj/prime_meridian.rb +65 -39
  49. data/lib/proj/projection.rb +698 -207
  50. data/lib/proj/session.rb +46 -0
  51. data/lib/proj/strings.rb +32 -0
  52. data/lib/proj/transformation.rb +101 -60
  53. data/lib/proj/unit.rb +108 -53
  54. data/lib/proj.rb +110 -9
  55. data/proj4rb.gemspec +5 -5
  56. data/test/abstract_test.rb +23 -1
  57. data/test/context_test.rb +172 -82
  58. data/test/conversion_test.rb +368 -0
  59. data/test/coordinate_system_test.rb +144 -0
  60. data/test/crs_test.rb +770 -71
  61. data/test/database_test.rb +360 -0
  62. data/test/datum_ensemble_test.rb +65 -0
  63. data/test/datum_test.rb +55 -0
  64. data/test/ellipsoid_test.rb +64 -18
  65. data/test/file_api_test.rb +66 -0
  66. data/test/grid_cache_test.rb +72 -0
  67. data/test/grid_test.rb +141 -0
  68. data/test/network_api_test.rb +45 -0
  69. data/test/operation_factory_context_test.rb +201 -0
  70. data/test/parameters_test.rb +40 -0
  71. data/test/pj_object_test.rb +179 -0
  72. data/test/prime_meridian_test.rb +76 -0
  73. data/test/proj_test.rb +46 -4
  74. data/test/projection_test.rb +646 -222
  75. data/test/session_test.rb +78 -0
  76. data/test/transformation_test.rb +149 -7
  77. data/test/unit_test.rb +57 -28
  78. metadata +51 -13
  79. data/lib/api/api_4_9.rb +0 -31
  80. data/lib/proj/config.rb +0 -70
  81. data/lib/proj/point.rb +0 -72
  82. data/test/prime_meridians_test.rb +0 -33
data/lib/proj/crs.rb CHANGED
@@ -1,204 +1,672 @@
1
- # encoding: UTF-8
2
- require 'stringio'
3
-
4
- module Proj
5
- # Represents a coordinate reference system.
6
- class Crs < PjObject
7
- # To create a coordinate system, you can use CRS codes, well-known text (WKT) strings
8
- # or old-style Proj4 strings (which are deprecated).
9
- #
10
- # @example
11
- # crs1 = Proj::Crs.new('EPSG:4326')
12
- # crs2 = Proj::Crs.new('urn:ogc:def:crs:EPSG::4326')
13
- # crs3 = Proj::Crs.new('+proj=longlat +datum=WGS84 +no_defs +type=crs')
14
- # crs4 = Proj::Crs.new(<<~EOS)
15
- # GEOGCRS["WGS 84",
16
- # DATUM["World Geodetic System 1984",
17
- # ELLIPSOID["WGS 84",6378137,298.257223563,
18
- # LENGTHUNIT["metre",1]]],
19
- # PRIMEM["Greenwich",0,
20
- # ANGLEUNIT["degree",0.0174532925199433]],
21
- # CS[ellipsoidal,2],
22
- # AXIS["geodetic latitude (Lat)",north,
23
- # ORDER[1],
24
- # ANGLEUNIT["degree",0.0174532925199433]],
25
- # AXIS["geodetic longitude (Lon)",east,
26
- # ORDER[2],
27
- # ANGLEUNIT["degree",0.0174532925199433]],
28
- # USAGE[
29
- # SCOPE["unknown"],
30
- # AREA["World"],
31
- # BBOX[-90,-180,90,180]],
32
- # ID["EPSG",4326]]
33
- # EOS
34
- #
35
- # Notice when using the old-style Proj4 string, the addition of the "+type=crs" value.
36
- #
37
- # @param value [String]. See above
38
- # @param context [Context]. An optional Context that the Crs will use for calculations.
39
- def initialize(value, context=nil)
40
- pointer = Api.proj_create(context || Context.current, value)
41
-
42
- if pointer.null?
43
- Error.check
44
- end
45
-
46
- super(pointer, context)
47
-
48
- if Api.method_defined?(:proj_is_crs) && !Api.proj_is_crs(pointer)
49
- raise(Error, "Invalid crs definition. Proj created an instance of: #{self.proj_type}.")
50
- end
51
- end
52
-
53
- # Get the geodeticCRS / geographicCRS from a CRS.
54
- #
55
- # @return [Crs]
56
- def geodetic_crs
57
- PjObject.new(Api.proj_crs_get_geodetic_crs(self.context, self))
58
- end
59
-
60
- # Get a CRS component from a CompoundCRS.
61
- #
62
- # @return [Crs]
63
- def sub_crs(index)
64
- PjObject.new(Api.proj_crs_get_sub_crs(self.context, self, index))
65
- end
66
-
67
- # Returns the datum of a SingleCRS.
68
- #
69
- # @return [Crs]
70
- def datum
71
- PjObject.new(Api.proj_crs_get_datum(self.context, self))
72
- end
73
-
74
- # Get the horizontal datum from a CRS.
75
- #
76
- # @return [Crs]
77
- def horizontal_datum
78
- PjObject.new(Api.proj_crs_get_horizontal_datum(self.context, self))
79
- end
80
-
81
- # Returns the coordinate system of a SingleCRS.
82
- #
83
- # @return [Crs]
84
- def coordinate_system
85
- PjObject.new(Api.proj_crs_get_coordinate_system(self.context, self))
86
- end
87
-
88
- # Returns the number of axis of the coordinate system.
89
- #
90
- # @return [Integer]
91
- def axis_count
92
- result = Api.proj_cs_get_axis_count(self.context, self.coordinate_system)
93
- if result == -1
94
- Error.check
95
- end
96
- result
97
- end
98
-
99
- # Returns information on an axis.
100
- #
101
- # @return [Array<Hash>]
102
- def axis_info
103
- self.axis_count.times.map do |index|
104
- p_name = FFI::MemoryPointer.new(:pointer)
105
- p_abbreviation = FFI::MemoryPointer.new(:pointer)
106
- p_direction = FFI::MemoryPointer.new(:pointer)
107
- p_unit_conv_factor = FFI::MemoryPointer.new(:double)
108
- p_unit_name = FFI::MemoryPointer.new(:pointer)
109
- p_unit_auth_name = FFI::MemoryPointer.new(:pointer)
110
- p_unit_code = FFI::MemoryPointer.new(:pointer)
111
-
112
- result = Api.proj_cs_get_axis_info(self.context, self.coordinate_system, index,
113
- p_name, p_abbreviation, p_direction, p_unit_conv_factor, p_unit_name, p_unit_auth_name, p_unit_code)
114
-
115
- unless result
116
- Error.check
117
- end
118
-
119
- {:name => p_name.read_pointer.read_string,
120
- :abbreviation => p_abbreviation.read_pointer.read_string_to_null,
121
- :direction => p_direction.read_pointer.read_string_to_null,
122
- :unit_conv_factor => p_unit_conv_factor.read_double,
123
- :unit_name => p_unit_name.read_pointer.read_string_to_null,
124
- :unit_auth_name => p_unit_auth_name.read_pointer.read_string_to_null,
125
- :unit_code => p_unit_code.read_pointer.read_string_to_null}
126
- end
127
- end
128
-
129
- # Returns the type of the coordinate system.
130
- #
131
- # @return [:PJ_COORDINATE_SYSTEM_TYPE]
132
- def crs_type
133
- result = Api.proj_cs_get_type(self.context, self.coordinate_system)
134
- if result == :PJ_CS_TYPE_UNKNOWN
135
- Error.check
136
- end
137
- result
138
- end
139
-
140
- # Return the area of use of an object.
141
- #
142
- # @return [Area]
143
- def area
144
- @area ||= Area.for_object(self)
145
- end
146
-
147
- # Get the ellipsoid from a CRS or a GeodeticReferenceFrame.
148
- #
149
- # @return [PjObject]
150
- def ellipsoid
151
- PjObject.new(Api.proj_get_ellipsoid(self.context, self))
152
- end
153
-
154
- # Return the Conversion of a DerivedCRS (such as a ProjectedCRS), or the Transformation from
155
- # the baseCRS to the hubCRS of a BoundCRS.
156
- #
157
- # @return [PjObject]
158
- def operation
159
- pointer = Api.proj_crs_get_coordoperation(self.context, self)
160
- if pointer.null?
161
- Error.check
162
- end
163
- PjObject.new(pointer)
164
- end
165
-
166
- # Get the prime meridian of a CRS or a GeodeticReferenceFrame.
167
- #
168
- # @return [PjObject]
169
- def prime_meridian
170
- PjObject.new(Api.proj_get_prime_meridian(self.context, self))
171
- end
172
-
173
- # A nicely printed out description
174
- #
175
- # @return [String]
176
- def inspect
177
- result = StringIO.new
178
- result.set_encoding('UTF-8')
179
- result << <<~EOS
180
- <#{self.class.name}>: #{self.auth(0)}
181
- #{self.description}
182
- Axis Info [#{self.crs_type}]:
183
- EOS
184
-
185
- self.axis_info.each do |axis_info|
186
- result << "- #{axis_info[:abbreviation]}[#{axis_info[:direction]}]: #{axis_info[:name]} (#{axis_info[:unit_name]})" << "\n"
187
- end
188
-
189
- result << <<~EOS
190
- Area of Use:
191
- - name: #{self.area.name}
192
- - bounds: (#{self.area.west_lon_degree}, #{self.area.south_lat_degree}, #{self.area.east_lon_degree}, #{self.area.north_lat_degree})
193
- Coordinate operation:
194
- - name: ?
195
- - method: ?
196
- Datum: #{self.datum.name}
197
- - Ellipsoid: #{self.ellipsoid.name}
198
- - Prime Meridian: #{self.prime_meridian.name}
199
- EOS
200
-
201
- result.string
202
- end
203
- end
204
- end
1
+ # encoding: UTF-8
2
+ require 'stringio'
3
+
4
+ module Proj
5
+ # Represents a coordinate reference system.
6
+ class Crs < PjObject
7
+ # Create a ProjectedCRS.
8
+ #
9
+ # @param ctx [Context] Context
10
+ # @param name [String] Name of the GeographicCRS. Default is nil.
11
+ # @param geodetic_crs [CRS] Base GeodeticCRS
12
+ # @param conversion [Conversion] Conversion
13
+ # @param coordinate_system [CoordinateSystem] Cartesian coordinate system
14
+ #
15
+ # @return [CRS]
16
+ def self.create_projected(context, name: nil, geodetic_crs:, conversion:, coordinate_system:)
17
+ pointer = Api.proj_create_projected_crs(context, name, geodetic_crs, conversion, coordinate_system)
18
+
19
+ if pointer.null?
20
+ Error.check_context(context)
21
+ end
22
+
23
+ self.create_object(pointer, context)
24
+ end
25
+
26
+ # Returns a BoundCRS
27
+ #
28
+ # @param ctx [Context] Context
29
+ # @param base_crs [CRS] Base CRS
30
+ # @param hub_crs [CRS] HUB CRS
31
+ # @param transformation [Transformation]
32
+ #
33
+ # @return [CRS]
34
+ def self.create_bound(context, base_crs:, hub_crs:, transformation:)
35
+ pointer = Api.proj_crs_create_bound_crs(context, base_crs, hub_crs, transformation)
36
+
37
+ if pointer.null?
38
+ Error.check_context(context)
39
+ end
40
+
41
+ self.create_object(pointer, context)
42
+ end
43
+
44
+ # Returns a BoundCRS with a transformation to EPSG:4326 wrapping it
45
+ #
46
+ # @param ctx [Context] Context
47
+ # @param crs [CRS] CRS to wrap
48
+ # @param allow_intermediate_crs [String] Specifies if an intermediate CRS may be considered when
49
+ # computing the possible transformations. Allowed values are:
50
+ # * ALWAYS
51
+ # * IF_NO_DIRECT_TRANSFORMATION
52
+ # * NEVER
53
+ #
54
+ # Default is NEVER
55
+ #
56
+ # @return [CRS]
57
+ def self.create_bound_to_wgs84(context, crs:, allow_intermediate_crs: "NEVER")
58
+ options = {"ALLOW_INTERMEDIATE_CRS": allow_intermediate_crs}
59
+ options_ptr = create_options_pointer(options)
60
+
61
+ pointer = Api.proj_crs_create_bound_crs_to_WGS84(context, crs, options_ptr)
62
+
63
+ if pointer.null?
64
+ Error.check_context(context)
65
+ end
66
+
67
+ self.create_object(pointer, context)
68
+ end
69
+
70
+ # Create a a EngineeringCRS
71
+ #
72
+ # @param ctx [Context] Context
73
+ # @param name [String] Name of the CRS. Default is nil.
74
+ #
75
+ # @return [CRS]
76
+ def self.create_engineering(context, name:)
77
+ pointer = Api.proj_create_engineering_crs(context, name)
78
+
79
+ if pointer.null?
80
+ Error.check_context(context)
81
+ end
82
+
83
+ self.create_object(pointer, context)
84
+ end
85
+
86
+ # Create a VerticalCRS. For additional functionality see Crs#create_vertical_ex
87
+ #
88
+ # @param ctx [Context] Context
89
+ # @param name [String] Name of the GeographicCRS. Default is nil.
90
+ # @param datum_name [String] Name of the GeodeticReferenceFrame. Default is nil.
91
+ # @param linear_units [String] Name of the angular units. Or nil for meters.
92
+ # @param linear_units_conv [Double] Conversion factor from linear units to meters. Default is 0 if linear_units is nil
93
+ #
94
+ # @return [CRS]
95
+ def self.create_vertical(context, name:, datum_name:, linear_units:, linear_units_conv:)
96
+ pointer = Api.proj_create_vertical_crs(context, name, datum_name, linear_units, linear_units_conv)
97
+
98
+ if pointer.null?
99
+ Error.check_context(context)
100
+ end
101
+
102
+ self.create_object(pointer, context)
103
+ end
104
+
105
+ # Create a Bound Vertical CRS, with a transformation to a hub geographic 3D crs, such as
106
+ # EPSG:4979 or WGS84, using a grid
107
+ #
108
+ # @param ctx [Context] Context
109
+ # @param vertical_crs [CRS] A VerticalCRS
110
+ # @param hub_geographic_3d_crs [CRS] A Geographic 3D CRS
111
+ # @param grid_name [String] Grid name (typically a .gtx file)
112
+ #
113
+ # @return [CRS]
114
+ def self.create_bound_vertical(context, vertical_crs:, hub_crs:, grid_name:)
115
+ pointer = Api.proj_crs_create_bound_vertical_crs(context, vertical_crs, hub_crs, grid_name)
116
+
117
+ if pointer.null?
118
+ Error.check_context(context)
119
+ end
120
+
121
+ self.create_object(pointer, context)
122
+ end
123
+
124
+ # Create a VerticalCRS. This is an extended version of Crs#create_vertical that adds
125
+ # the capability of defining a geoid model.
126
+ #
127
+ # @param ctx [Context] Context
128
+ # @param name [String] Name of the GeographicCRS. Default is nil.
129
+ # @param datum_name [String] Name of the GeodeticReferenceFrame. Default is nil.
130
+ # @param datum_auth_name [String] Authority name of the VerticalReferenceFrame. Default is nil.
131
+ # @param datum_code [String] Code of the VerticalReferenceFrame. Default is nil.
132
+ # @param linear_units [String] Name of the angular units. Or nil for meters.
133
+ # @param linear_units_conv [Double] Conversion factor from linear units to meters. Default is 0 if linear_units is nil
134
+ # @param geoid_model_name [String] Geoid model name. Can be a name from the geoid_model name or a string "PROJ foo.gtx". Default is nil.
135
+ # @param geoid_model_auth_name [String] Authority name of the transformation for the geoid model. Default is nil.
136
+ # @param geoid_model_code [String] Code of the transformation for the geoid model. Default is nil.
137
+ # @param geoid_geog_crs [Crs] Geographic CRS for the geoid transformation. Default is nil.
138
+ # @param accuracy [Double] Accuracy in meters. Default is nil
139
+ #
140
+ # @return [CRS]
141
+ def self.create_vertical_ex(context, name: nil, datum_name: nil, datum_auth_name: nil, datum_code: nil,
142
+ linear_units: nil, linear_units_conv: 0,
143
+ geoid_model_name: nil, geoid_model_auth_name: nil, geoid_model_code: nil,
144
+ geoid_geog_crs: nil, accuracy: nil)
145
+
146
+ options = {"ACCURACY": accuracy.nil? ? nil : accuracy.to_s}
147
+ options_ptr = create_options_pointer(options)
148
+
149
+ pointer = Api.proj_create_vertical_crs_ex(context, name, datum_name, datum_auth_name, datum_code, linear_units, linear_units_conv, geoid_model_name, geoid_model_auth_name, geoid_model_code, geoid_geog_crs, options_ptr)
150
+
151
+ if pointer.null?
152
+ Error.check_context(context)
153
+ end
154
+
155
+ self.create_object(pointer, context)
156
+ end
157
+
158
+ # Create a CompoundCRS.
159
+ #
160
+ # @param ctx [Context] Context
161
+ # @param name [String] Name of the GeographicCRS. Default is nil.
162
+ # @param horizontal_crs [CRS] A horizontal CRS
163
+ # @param vertical_crs [CRS] A vertical CRS
164
+ #
165
+ # @return [CRS]
166
+ def self.create_compound(context, name:, horizontal_crs:, vertical_crs:)
167
+ pointer = Api.proj_create_compound_crs(context, name, horizontal_crs, vertical_crs)
168
+
169
+ if pointer.null?
170
+ Error.check_context(context)
171
+ end
172
+
173
+ self.create_object(pointer, context)
174
+ end
175
+
176
+ # Create a GeographicCRS.
177
+ #
178
+ # @param ctx [Context] Context
179
+ # @param name [String] Name of the GeographicCRS. Default is nil.
180
+ # @param datum_name [String] Name of the GeodeticReferenceFrame. Default is nil.
181
+ # @param ellipsoid_name [String] Name of the Ellipsoid. Default is nil.
182
+ # @param semi_major_meter [Double] Ellipsoid semi-major axis, in meters.
183
+ # @param inv_flattening [Double] Ellipsoid inverse flattening. Or 0 for a sphere.
184
+ # @param prime_meridian_name [String] Name of the PrimeMeridian. Default is nil.
185
+ # @param prime_meridian_offset [Double] Offset of the prime meridian, expressed in the specified angular units.
186
+ # @param pm_angular_units [String] Name of the angular units. Or nil for degrees.
187
+ # @param pm_angular_units_conv [Double] Conversion factor from the angular unit to radians. Default is 0 if pm_angular_units is nil
188
+ # @param coordinate_system [CoordinateSystem] Ellipsoidal coordinate system
189
+ #
190
+ # @return [CRS]
191
+ def self.create_geographic(context, name:, datum_name:, ellps_name:, semi_major_meter:, inv_flattening:, prime_meridian_name:, prime_meridian_offset:, pm_angular_units:, pm_units_conv:, coordinate_system:)
192
+ pointer = Api.proj_create_geographic_crs(context, name, datum_name, ellps_name, semi_major_meter, inv_flattening, prime_meridian_name, prime_meridian_offset, pm_angular_units, pm_units_conv, coordinate_system)
193
+
194
+ if pointer.null?
195
+ Error.check_context(context)
196
+ end
197
+
198
+ self.create_object(pointer, context)
199
+ end
200
+
201
+ # Create a GeographicCRS from a datum
202
+ #
203
+ # @param ctx [Context] Context
204
+ # @param name [String] Name of the GeographicCRS. Default is nil.
205
+ # @param datum [Datum | DatumEnsemble] Datum or DatumEnsemble
206
+ # @param coordinate_system [CoordinateSystem] Ellipsoidal coordinate system
207
+ #
208
+ # @return [CRS]
209
+ def self.create_geographic_from_datum(context, name:, datum:, coordinate_system:)
210
+ pointer = Api.proj_create_geographic_crs_from_datum(context, name, datum, coordinate_system)
211
+
212
+ if pointer.null?
213
+ Error.check_context(context)
214
+ end
215
+
216
+ self.create_object(pointer, context)
217
+ end
218
+
219
+ # Create a GeographicCRS.
220
+ #
221
+ # @param ctx [Context] Context
222
+ # @param name [String] Name of the GeographicCRS. Default is nil.
223
+ # @param datum_name [String] Name of the GeodeticReferenceFrame. Default is nil.
224
+ # @param ellipsoid_name [String] Name of the Ellipsoid. Default is nil.
225
+ # @param semi_major_meter [Double] Ellipsoid semi-major axis, in meters.
226
+ # @param inv_flattening [Double] Ellipsoid inverse flattening. Or 0 for a sphere.
227
+ # @param prime_meridian_name [String] Name of the PrimeMeridian. Default is nil.
228
+ # @param prime_meridian_offset [Double] Offset of the prime meridian, expressed in the specified angular units.
229
+ # @param angular_units [String] Name of the angular units. Or nil for degrees.
230
+ # @param angular_units_conv [Double] Conversion factor from the angular unit to radians. Default is 0 if angular_units is nil
231
+ # @param linear_units [String] Name of the angular units. Or nil for meters.
232
+ # @param linear_units_conv [Double] Conversion factor from linear units to meters. Default is 0 if linear_units is nil
233
+ #
234
+ # @return [CRS]
235
+ def self.create_geocentric(context, name:, datum_name:, ellps_name:, semi_major_meter:, inv_flattening:, prime_meridian_name:, prime_meridian_offset:, angular_units:, angular_units_conv:, linear_units:, linear_units_conv:)
236
+ pointer = Api.proj_create_geocentric_crs(context, name, datum_name, ellps_name, semi_major_meter, inv_flattening, prime_meridian_name, prime_meridian_offset, angular_units, angular_units_conv, linear_units, linear_units_conv)
237
+
238
+ if pointer.null?
239
+ Error.check_context(context)
240
+ end
241
+
242
+ self.create_object(pointer, context)
243
+ end
244
+
245
+ # Create a GeodeticCRS of geocentric type
246
+ #
247
+ # @param ctx [Context] Context
248
+ # @param name [String] Name of the GeographicCRS. Default is nil.
249
+ # @param datum [Datum | DatumEnsemble] Datum or DatumEnsemble
250
+ # @param linear_units [String] Name of the angular units. Or nil for meters.
251
+ # @param linear_units_conv [Double] Conversion factor from linear units to meters. Default is 0 if linear_units is nil
252
+ #
253
+ # @return [CRS]
254
+ def self.create_geocentric_from_datum(context, name:, datum:, linear_units:, linear_units_conv:)
255
+ pointer = Api.proj_create_geocentric_crs_from_datum(context, name, datum, linear_units, linear_units_conv)
256
+
257
+ if pointer.null?
258
+ Error.check_context(context)
259
+ end
260
+
261
+ self.create_object(pointer, context)
262
+ end
263
+
264
+ # Create a DerivedGeograhicCRS
265
+ #
266
+ # @param ctx [Context] Context
267
+ # @param name [String] Name of the GeographicCRS. Default is nil.
268
+ # @param base_geographic_crs [CRS] Base Geographic CRS
269
+ # @param conversion [Conversion] Conversion from the base Geographic to the DerivedGeograhicCRS
270
+ # @param coordinate_system [CoordinateSystem] Ellipsoidal coordinate system
271
+ #
272
+ # @return [CRS]
273
+ def self.create_derived_geographic(context, name: nil, base_geographic_crs:, conversion:, coordinate_system:)
274
+ pointer = Api.proj_create_derived_geographic_crs(context, name, base_geographic_crs, conversion, coordinate_system)
275
+
276
+ if pointer.null?
277
+ Error.check_context(context)
278
+ end
279
+
280
+ self.create_object(pointer, context)
281
+ end
282
+
283
+ # Find GeodeticCRSes that use the specified datum
284
+ #
285
+ # @param ctx [Context] Context
286
+ # @param auth_name [string] - Authority name. Default is nil.
287
+ # @param datum_auth_name [String] Datum authority name
288
+ # @param datum_code [String] Datum code
289
+ # @param crs_type [String] The CRS type. Default is nil. Allowed values are:
290
+ # * geographic 2D
291
+ # * geographic 3D
292
+ # * geocentric
293
+ #
294
+ # @return [PjObjects] - A list of CRSes
295
+ def self.query_geodetic_from_datum(context, auth_name: nil, datum_auth_name:, datum_code:, crs_type: nil)
296
+ pointer = Api.proj_query_geodetic_crs_from_datum(context, auth_name, datum_auth_name, datum_code, crs_type)
297
+
298
+ if pointer.null?
299
+ Error.check_context(context)
300
+ end
301
+
302
+ PjObjects.new(pointer, context)
303
+ end
304
+
305
+ # To create a coordinate system, you can use CRS codes, well-known text (WKT) strings
306
+ # or old-style Proj4 strings (which are deprecated).
307
+ #
308
+ # @example
309
+ # crs1 = Proj::Crs.new('EPSG:4326')
310
+ # crs2 = Proj::Crs.new('urn:ogc:def:crs:EPSG::4326')
311
+ # crs3 = Proj::Crs.new('+proj=longlat +datum=WGS84 +no_defs +type=crs')
312
+ # crs4 = Proj::Crs.new(<<~EOS)
313
+ # GEOGCRS["WGS 84",
314
+ # DATUM["World Geodetic System 1984",
315
+ # ELLIPSOID["WGS 84",6378137,298.257223563,
316
+ # LENGTHUNIT["meter",1]]],
317
+ # PRIMEM["Greenwich",0,
318
+ # ANGLEUNIT["degree",0.0174532925199433]],
319
+ # CS[ellipsoidal,2],
320
+ # AXIS["geodetic latitude (Lat)",north,
321
+ # ORDER[1],
322
+ # ANGLEUNIT["degree",0.0174532925199433]],
323
+ # AXIS["geodetic longitude (Lon)",east,
324
+ # ORDER[2],
325
+ # ANGLEUNIT["degree",0.0174532925199433]],
326
+ # USAGE[
327
+ # SCOPE["unknown"],
328
+ # AREA["World"],
329
+ # BBOX[-90,-180,90,180]],
330
+ # ID["EPSG",4326]]
331
+ # EOS
332
+ #
333
+ # Notice when using the old-style Proj4 string, the addition of the "+type=crs" value.
334
+ #
335
+ # @param value [String]. See above
336
+ # @param context [Context]. An optional Context that the Crs will use for calculations.
337
+ def initialize(value, context=nil)
338
+ ptr = Api.proj_create(context || Context.current, value)
339
+
340
+ if ptr.null?
341
+ Error.check_object(self)
342
+ end
343
+
344
+ super(ptr, context)
345
+
346
+ if Api.method_defined?(:proj_is_crs) && !Api.proj_is_crs(ptr)
347
+ raise(Error, "Invalid crs definition. Proj created an instance of: #{self.proj_type}.")
348
+ end
349
+ end
350
+
351
+ # Get the geodeticCRS / geographicCRS from a CRS.
352
+ #
353
+ # @example
354
+ # crs = Proj::Crs.new('EPSG:4326')
355
+ # geodetic = crs.geodetic_crs
356
+ # assert_equal(:PJ_TYPE_GEOGRAPHIC_2D_CRS, geodetic.proj_type)
357
+ # assert_equal('+proj=longlat +datum=WGS84 +no_defs +type=crs', geodetic.to_proj_string)
358
+ #
359
+ # @see https://proj.org/development/reference/functions.html#c.proj_crs_get_geodetic_crs
360
+ #
361
+ # @return [CRS]
362
+ def geodetic_crs
363
+ pointer = Api.proj_crs_get_geodetic_crs(self.context, self)
364
+ self.class.create_object(pointer, self.context)
365
+ end
366
+
367
+ # Get a CRS component from a CompoundCRS.
368
+ #
369
+ # @see {https://proj.org/development/reference/functions.html#c.proj_crs_get_sub_crs} proj_crs_get_sub_crs
370
+ #
371
+ # @param index [Integer] Index of the CRS component (typically 0 = horizontal, 1 = vertical)
372
+ #
373
+ # @return [CRS]
374
+ def sub_crs(index)
375
+ pointer = Api.proj_crs_get_sub_crs(self.context, self, index)
376
+ self.class.create_object(pointer, self.context)
377
+ end
378
+
379
+ # Returns the datum of a SingleCRS.
380
+ #
381
+ # @see https://proj.org/development/reference/functions.html#c.proj_crs_get_datum
382
+ #
383
+ # @return [Datum]
384
+ def datum
385
+ ptr = Api.proj_crs_get_datum(self.context, self)
386
+
387
+ if ptr.null?
388
+ Error.check_object(self)
389
+ end
390
+
391
+ self.class.create_object(ptr, self.context)
392
+ end
393
+
394
+ # Returns a datum for a SingleCRS. If the SingleCRS has a datum, then this datum is returned.
395
+ # Otherwise, the SingleCRS has a datum ensemble, and this datum ensemble is returned as
396
+ # a regular datum instead of a datum ensemble.
397
+ #
398
+ # @see https://proj.org/development/reference/functions.html#c.proj_crs_get_datum_forced
399
+ #
400
+ # @return [Datum]
401
+ def datum_forced
402
+ pointer = Api.proj_crs_get_datum_forced(self.context, self)
403
+ self.class.create_object(pointer, self.context)
404
+ end
405
+
406
+ # Get the horizontal datum from a CRS.
407
+ #
408
+ # @see https://proj.org/development/reference/functions.html#c.proj_crs_get_horizontal_datum
409
+ #
410
+ # @return [CRS]
411
+ def horizontal_datum
412
+ pointer = Api.proj_crs_get_horizontal_datum(self.context, self)
413
+ self.class.create_object(pointer, self.context)
414
+ end
415
+
416
+ # Returns the {DatumEnsemble datum ensemble} of a SingleCRS.
417
+ #
418
+ # @see https://proj.org/development/reference/functions.html#c.proj_crs_get_datum_ensemble
419
+ #
420
+ # @return [DatumEnsemble]
421
+ def datum_ensemble
422
+ pointer = Api.proj_crs_get_datum_ensemble(self.context, self)
423
+ self.class.create_object(pointer, self.context)
424
+ end
425
+
426
+ # Returns the coordinate system of a SingleCRS.
427
+ #
428
+ # @return [CoordinateSystem]
429
+ def coordinate_system
430
+ pointer = Api.proj_crs_get_coordinate_system(self.context, self)
431
+ CoordinateSystem.new(pointer, self.context)
432
+ end
433
+
434
+ # Returns the ellipsoid
435
+ #
436
+ # @see https://proj.org/development/reference/functions.html#c.proj_get_ellipsoid
437
+ #
438
+ # @return [PjObject]
439
+ def ellipsoid
440
+ pointer = Api.proj_get_ellipsoid(self.context, self)
441
+ self.class.create_object(pointer, self.context)
442
+ end
443
+
444
+ # Returns whether a CRS is a derived CRS.
445
+ #
446
+ # @return [Boolean]
447
+ def derived?
448
+ result = Api.proj_crs_is_derived(self.context, self)
449
+ result == 1 ? true : false
450
+ end
451
+
452
+ # Return the Conversion of a DerivedCRS (such as a ProjectedCRS), or the Transformation from
453
+ # the baseCRS to the hubCRS of a BoundCRS.
454
+ #
455
+ # @return [PjObject]
456
+ def coordinate_operation
457
+ ptr = Api.proj_crs_get_coordoperation(self.context, self)
458
+ if ptr.null?
459
+ Error.check_object(self)
460
+ end
461
+ self.class.create_object(ptr, self.context)
462
+ end
463
+
464
+ # Returns the prime meridian
465
+ #
466
+ # @see https://proj.org/development/reference/functions.html#c.proj_get_prime_meridian
467
+ #
468
+ # @return [PjObject]
469
+ def prime_meridian
470
+ pointer = Api.proj_get_prime_meridian(self.context, self)
471
+ self.class.create_object(pointer, self.context)
472
+ end
473
+
474
+ # Returns a list of matching reference CRS, and the percentage (0-100) of confidence in the match.
475
+ #
476
+ # @param auth_name [string] - Authority name, or nil for all authorities
477
+ #
478
+ # @return [Array] - Array of CRS objects sorted by decreasing confidence.
479
+ def identify(auth_name)
480
+ confidences_out_ptr = FFI::MemoryPointer.new(:pointer)
481
+ pointer = Api.proj_identify(self.context, self, auth_name, nil, confidences_out_ptr)
482
+ objects = PjObjects.new(pointer, self.context)
483
+
484
+ # Get confidences and free the list
485
+ confidences_ptr = confidences_out_ptr.read_pointer
486
+ confidences = confidences_ptr.read_array_of_type(:int, :read_int, objects.count)
487
+ Api.proj_int_list_destroy(confidences_ptr)
488
+
489
+ return objects, confidences
490
+ end
491
+
492
+ # Returns whether a CRS is a Derived CRS
493
+ #
494
+ # @return [Boolean] - True if the CRS is derived
495
+ def derived?
496
+ result = Api.proj_is_derived_crs(self.context, self)
497
+ result == 1 ? true : false
498
+ end
499
+
500
+ # Return a copy of the CRS with its name changed
501
+ #
502
+ # @param name [String] The new name of the CRS
503
+ #
504
+ # @return [CRS]
505
+ def alter_name(name)
506
+ ptr = Api.proj_alter_name(self.context, self, name)
507
+
508
+ if ptr.null?
509
+ Error.check_object(self)
510
+ end
511
+
512
+ self.class.create_object(ptr, context)
513
+ end
514
+
515
+ # Return a copy of the CRS with its identifier changed
516
+ #
517
+ # @param name [String] The new authority name of the CRS
518
+ # @param code [String] The new code of the CRS
519
+ #
520
+ # @return [CRS]
521
+ def alter_id(auth_name, code)
522
+ ptr = Api.proj_alter_id(self.context, self, auth_name, code)
523
+
524
+ if ptr.null?
525
+ Error.check_object(self)
526
+ end
527
+
528
+ self.class.create_object(ptr, context)
529
+ end
530
+
531
+ # Return a copy of the CRS with its geodetic CRS changed.
532
+ # * If the CRS is a GeodeticCRS then a clone of new_geod_crs is returned.
533
+ # * If the CRS is a ProjectedCRS, then it replaces the base CRS with new_geod_crs.
534
+ # * If the CRS is a CompoundCRS, then it replaces the GeodeticCRS part of the horizontal
535
+ # CRS with new_geod_crs.
536
+ # * In other cases, it returns a clone of obj.
537
+ #
538
+ # @param new_geod_crs [CRS] A GeodeticCRS
539
+ #
540
+ # @return [CRS]
541
+ def alter_geodetic_crs(new_geod_crs)
542
+ ptr = Api.proj_crs_alter_geodetic_crs(self.context, self, new_geod_crs)
543
+
544
+ if ptr.null?
545
+ Error.check_object(self)
546
+ end
547
+
548
+ self.class.create_object(ptr, context)
549
+ end
550
+
551
+ # Return a copy of the CRS with its angular units changed
552
+ #
553
+ # @param angular_units [String] Name of the angular units. Or nil for degrees.
554
+ # @param angular_units_conv [Double] Conversion factor from the angular unit to radians. Default is 0 if angular_units is nil
555
+ # @param unit_auth_name [String] Unit authority name. Defaults to nil.
556
+ # @param unit_code [String] Unit code. Defaults to nil.
557
+ #
558
+ # @return [CRS]
559
+ def alter_cs_angular_unit(angular_units: nil, angular_units_conv: 0, unit_auth_name: nil, unit_code: nil)
560
+ ptr = Api.proj_crs_alter_cs_angular_unit(self.context, self, angular_units, angular_units_conv, unit_auth_name, unit_code)
561
+
562
+ if ptr.null?
563
+ Error.check_object(self)
564
+ end
565
+
566
+ self.class.create_object(ptr, context)
567
+ end
568
+
569
+ # Return a copy of the CRS with its linear units changed. The CRS
570
+ # must be or contain a ProjectedCRS, VerticalCRS or a GeocentricCRS.
571
+ #
572
+ # @param linear_units [String] Name of the linear units. Or nil for meters.
573
+ # @param linear_units_conv [Double] Conversion factor from the linear unit to meters. Default is 0 if linear_units is nil
574
+ # @param unit_auth_name [String] Unit authority name. Defaults to nil.
575
+ # @param unit_code [String] Unit code. Defaults to nil.
576
+ #
577
+ # @return [CRS]
578
+ def alter_cs_linear_unit(linear_units: nil, linear_units_conv: 0, unit_auth_name: nil, unit_code: nil)
579
+ ptr = Api.proj_crs_alter_cs_linear_unit(self.context, self, linear_units, linear_units_conv, unit_auth_name, unit_code)
580
+
581
+ if ptr.null?
582
+ Error.check_object(self)
583
+ end
584
+
585
+ self.class.create_object(ptr, context)
586
+ end
587
+
588
+ # Return a copy of the CRS with its linear units changed. The CRS
589
+ # must be or contain a ProjectedCRS, VerticalCRS or a GeocentricCRS.
590
+ #
591
+ # @param linear_units [String] Name of the linear units. Or nil for meters.
592
+ # @param linear_units_conv [Double] Conversion factor from the linear unit to meters. Default is 0 if linear_units is nil
593
+ # @param unit_auth_name [String] Unit authority name. Defaults to nil.
594
+ # @param unit_code [String] Unit code. Defaults to nil.
595
+ # @param convert_to_new_unit [Boolean] If true then existing values will be converted from
596
+ # their current unit to the new unit. If false then their values will be left unchanged
597
+ # and the unit overridden (so the resulting CRS will not be equivalent to the
598
+ # original one for reprojection purposes).
599
+ #
600
+ # @return [CRS]
601
+ def alter_parameters_linear_unit(linear_units: nil, linear_units_conv: 0, unit_auth_name: nil, unit_code: nil, convert_to_new_unit:)
602
+ ptr = Api.proj_crs_alter_parameters_linear_unit(self.context, self, linear_units, linear_units_conv,
603
+ unit_auth_name, unit_code,
604
+ convert_to_new_unit ? 1 : 0)
605
+
606
+ if ptr.null?
607
+ Error.check_object(self)
608
+ end
609
+
610
+ self.class.create_object(ptr, context)
611
+ end
612
+
613
+ # Create a 3D CRS from this 2D CRS. The new axis will be ellipsoidal height,
614
+ # oriented upwards, and with metre units.
615
+ #
616
+ # @param name [String] CRS name. If nil then the name of this CRS will be used.
617
+ #
618
+ # @return [CRS]
619
+ def promote_to_3d(name: nil)
620
+ ptr = Api.proj_crs_promote_to_3D(self.context, name, self)
621
+
622
+ if ptr.null?
623
+ Error.check_object(self)
624
+ end
625
+
626
+ self.class.create_object(ptr, context)
627
+ end
628
+
629
+ # Create a 2D CRS from an this 3D CRS.
630
+ #
631
+ # @param name [String] CRS name. If nil then the name of this CRS will be used.
632
+ #
633
+ # @return [CRS]
634
+ def demote_to_2d(name: nil)
635
+ ptr = Api.proj_crs_demote_to_2D(self.context, name, self)
636
+
637
+ if ptr.null?
638
+ Error.check_object(self)
639
+ end
640
+
641
+ self.class.create_object(ptr, context)
642
+ end
643
+
644
+ # Create a projected 3D CRS from this projected 2D CRS.
645
+ #
646
+ # This CRS's name is replaced by the name parameter and its base geographic CRS
647
+ # is replaced by geog_3D_crs. The vertical axis of geog_3D_crs (ellipsoidal height)
648
+ # will be added as the 3rd axis of the resulting projected 3D CRS.
649
+ #
650
+ # Normally, the passed geog_3D_crs should be the 3D counterpart of the original
651
+ # 2D base geographic CRS of projected_2D_crs, but such no check is done.
652
+ #
653
+ # It is also possible to invoke this function with a nil geog_3D_crs. In this case
654
+ # the existing base geographic of this CRS will be automatically promoted to 3D by
655
+ # assuming a 3rd axis being an ellipsoidal height, oriented upwards, and with metre units.
656
+ # This is equivalent to using Crs#promote_to_3d
657
+ #
658
+ # @param name [String] CRS name. If nil then the name of this CRS will be used.
659
+ # @param geog_3D_crs [CRS] Base geographic 3D CRS for the new CRS. Defaults to nil.
660
+ #
661
+ # @return [CRS]
662
+ def projected_3d(name: nil, geog_3d_crs: nil)
663
+ ptr = Api.proj_crs_create_projected_3D_crs_from_2D(self.context, name, self, geog_3d_crs)
664
+
665
+ if ptr.null?
666
+ Error.check_object(self)
667
+ end
668
+
669
+ self.class.create_object(ptr, context)
670
+ end
671
+ end
672
+ end