proj4rb 3.0.0 → 4.0.0

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.
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