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