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
@@ -1,381 +1,464 @@
1
- # encoding: UTF-8
2
- require 'stringio'
3
-
4
- module Proj
5
- Param = Struct.new(:name, :auth_name, :code, :value, :value_string,
6
- :unit_conv_factor, :unit_name, :unit_auth_name, :unit_code, :unit_category,
7
- keyword_init: true)
8
-
9
- # Coordinate Operations convert {Coordinate coordinates} to a new value. In Proj they are
10
- # can either by {Conversion conversions} that do not exert a change in reference frame
11
- # or {Transformation transformations} which do.
12
- module CoordinateOperationMixin
13
- # Return whether a coordinate operation can be instantiated as a PROJ pipeline, checking in particular that referenced grids are available.
14
- #
15
- # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_is_instantiable
16
- #
17
- # @return [Boolean]
18
- def instantiable?
19
- result = Api.proj_coordoperation_is_instantiable(self.context, self)
20
- result == 1 ? true : false
21
- end
22
-
23
- # Returns a coordinate operation that represents the inverse operation of this operation
24
- #
25
- # @return [Conversion, Transformation] Returns nil on error
26
- def create_inverse
27
- ptr = Api.proj_coordoperation_create_inverse(self.context, self)
28
- self.class.create_object(ptr, self.context)
29
- end
30
-
31
- # @!visibility private
32
- def method_info
33
- out_method_name = FFI::MemoryPointer.new(:string)
34
- out_method_auth_name = FFI::MemoryPointer.new(:string)
35
- out_method_code = FFI::MemoryPointer.new(:string)
36
-
37
- result = Api.proj_coordoperation_get_method_info(self.context, self, out_method_name, out_method_auth_name, out_method_code)
38
-
39
- if result != 1
40
- Error.check_object(self)
41
- end
42
-
43
- {:method_name => out_method_name.read_pointer.read_string_to_null,
44
- :method_auth_name => out_method_auth_name.read_pointer.read_string_to_null,
45
- :method_code => out_method_code.read_pointer.read_string_to_null}
46
- end
47
-
48
- # Returns the operation name
49
- #
50
- # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_method_info
51
- #
52
- # @return [String]
53
- def method_name
54
- method_info[:method_name]
55
- end
56
-
57
- # Returns the operation authority name
58
- #
59
- # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_method_info
60
- #
61
- # @return [String]
62
- def method_auth_name
63
- method_info[:method_auth_name]
64
- end
65
-
66
- # Returns the operation code
67
- #
68
- # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_method_info
69
- #
70
- # @return [String]
71
- def method_code
72
- method_info[:method_code]
73
- end
74
-
75
- # Returns the number of parameters of a SingleOperation
76
- #
77
- # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_param_count
78
- #
79
- # @return [Integer]
80
- def param_count
81
- Api.proj_coordoperation_get_param_count(self.context, self)
82
- end
83
-
84
- # Returns the index of a parameter of a SingleOperation
85
- #
86
- # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_param_index
87
- #
88
- # @param name [String] Name of the parameter. Must not be nil
89
- #
90
- # @return [Integer] Index of the parameter or -1 in case of error.
91
- def param_index(name)
92
- Api.proj_coordoperation_get_param_index(self.context, self, name)
93
- end
94
-
95
- # Returns a parameter of a SingleOperation
96
- #
97
- # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_param
98
- #
99
- # @param index [Integer] Parameter index
100
- #
101
- # @return [Param]
102
- def param(index)
103
- out_name = FFI::MemoryPointer.new(:string)
104
- out_auth_name = FFI::MemoryPointer.new(:string)
105
- out_code = FFI::MemoryPointer.new(:string)
106
- out_value = FFI::MemoryPointer.new(:double)
107
- out_value_string = FFI::MemoryPointer.new(:string)
108
- out_unit_conv_factor = FFI::MemoryPointer.new(:double)
109
- out_unit_name = FFI::MemoryPointer.new(:string)
110
- out_unit_auth_name = FFI::MemoryPointer.new(:string)
111
- out_unit_code = FFI::MemoryPointer.new(:string)
112
- out_unit_category = FFI::MemoryPointer.new(:string)
113
-
114
- result = Api.proj_coordoperation_get_param(self.context, self, index,
115
- out_name, out_auth_name, out_code,
116
- out_value, out_value_string,
117
- out_unit_conv_factor,
118
- out_unit_name, out_unit_auth_name,out_unit_code,
119
- out_unit_category)
120
- if result != 1
121
- Error.check_object(self)
122
- end
123
-
124
- name_ptr = out_name.read_pointer
125
- auth_name_ptr = out_auth_name.read_pointer
126
- code_ptr = out_code.read_pointer
127
- value_string_ptr = out_value_string.read_pointer
128
- unit_name_ptr = out_unit_name.read_pointer
129
- unit_auth_name_ptr = out_unit_auth_name.read_pointer
130
- unit_code_ptr = out_unit_code.read_pointer
131
- unit_category_ptr = out_unit_category.read_pointer
132
-
133
- Param.new(name: name_ptr.null? ? nil : name_ptr.read_string_to_null,
134
- auth_name: auth_name_ptr.null? ? nil : auth_name_ptr.read_string_to_null,
135
- code: code_ptr.null? ? nil : code_ptr.read_string_to_null,
136
- value: out_value.null? ? nil : out_value.read_double,
137
- value_string: value_string_ptr.null? ? nil : value_string_ptr.read_string_to_null,
138
- unit_conv_factor: out_unit_conv_factor.null? ? nil : out_unit_conv_factor.read_double,
139
- unit_name: unit_name_ptr.null? ? nil : unit_name_ptr.read_string_to_null,
140
- unit_auth_name: unit_auth_name_ptr.null? ? nil : unit_auth_name_ptr.read_string_to_null,
141
- unit_code: unit_code_ptr.null? ? nil : unit_code_ptr.read_string_to_null,
142
- unit_category: unit_category_ptr.null? ? nil : unit_category_ptr.read_string_to_null)
143
- end
144
-
145
- # Transforms a {Coordinate} from the source {Crs} to the target {Crs}. Coordinates should be expressed in
146
- # the units and axis order of the definition of the source CRS. The returned transformed coordinate will
147
- # be in the units and axis order of the definition of the target CRS.
148
- #
149
- # @see https://proj.org/development/reference/functions.html#c.proj_trans
150
- #
151
- # @param coord [Coordinate]
152
- #
153
- # @return [Coordinate]
154
- def forward(coord)
155
- self.transform(coord, :PJ_FWD)
156
- end
157
-
158
- # Transforms a {Coordinate} from the target {Crs} to the source {Crs}. Coordinates should be expressed in
159
- # the units and axis order of the definition of the source CRS. The returned transformed coordinate will
160
- # be in the units and axis order of the definition of the target CRS.
161
- #
162
- # @see https://proj.org/development/reference/functions.html#c.proj_trans
163
- #
164
- # @param coord [Coordinate]
165
- #
166
- # @return [Coordinate]
167
- def inverse(coord)
168
- self.transform(coord, :PJ_INV)
169
- end
170
-
171
- # Transforms a {Coordinate} in the specified direction. See {CoordinateOperationMixin#forward forward} and
172
- # {CoordinateOperationMixin#inverse inverse}
173
- #
174
- # @see https://proj.org/development/reference/functions.html#c.proj_trans
175
- #
176
- # @param direction [PJ_DIRECTION] Direction of transformation (:PJ_FWD or :PJ_INV)
177
- # @param coord [Coordinate]
178
- #
179
- # @return [Coordinate]
180
- def transform(coord, direction)
181
- struct = Api.proj_trans(self, direction, coord)
182
- Coordinate.from_coord(struct)
183
- end
184
-
185
- # Transform boundary densifying the edges to account for nonlinear transformations along these edges
186
- # and extracting the outermost bounds.
187
- #
188
- # @see https://proj.org/development/reference/functions.html#c.proj_trans_bounds
189
- #
190
- # @param bounds [Area] Bounding box in source CRS (target CRS if direction is inverse).
191
- # @param direction [PJ_DIRECTION] The direction of the transformation.
192
- # @param densify_points [Integer] Recommended to use 21. This is the number of points to use to densify the bounding polygon in the transformation.
193
- #
194
- # @return [Area] Bounding box in target CRS (target CRS if direction is inverse).
195
- def transform_bounds(bounds, direction, densify_points = 21)
196
- out_xmin = FFI::MemoryPointer.new(:double)
197
- out_ymin = FFI::MemoryPointer.new(:double)
198
- out_xmax = FFI::MemoryPointer.new(:double)
199
- out_ymax = FFI::MemoryPointer.new(:double)
200
-
201
- result = Api.proj_trans_bounds(self.context, self, direction,
202
- bounds.xmin, bounds.ymin, bounds.xmax, bounds.ymax,
203
- out_xmin, out_ymin, out_xmax, out_ymax, densify_points)
204
-
205
- unless result == 0
206
- Error.check_object(self)
207
- end
208
-
209
- Bounds.new(out_xmin.read_double, out_ymin.read_double, out_xmax.read_double, out_ymax.read_double)
210
- end
211
-
212
- # Transforms an array of {Coordinate coordinates}. Individual points that fail to transform
213
- # will have their components set to Infinity.
214
- #
215
- # @param coordinates [Array<Coordinate>] Coordinates to transform
216
- # @param direction [PJ_DIRECTION] The direction of the transformation
217
- #
218
- # @return [Array<Coordinate>] Array of transformed coordinates
219
- def transform_array(coordinates, direction)
220
- coords_ptr = FFI::MemoryPointer.new(Api::PJ_COORD, coordinates.size)
221
- coordinates.each_with_index do |coordinate, i|
222
- pj_coord = Api::PJ_COORD.new(coords_ptr[i])
223
- pj_coord.to_ptr.__copy_from__(coordinate.to_ptr, Api::PJ_COORD.size)
224
- end
225
-
226
- int = Api.proj_trans_array(self, direction, coordinates.size, coords_ptr)
227
- unless int == 0
228
- Error.check_object(self)
229
- end
230
-
231
- result = Array.new(coordinates.size)
232
- 0.upto(coordinates.size) do |i|
233
- pj_coord = Api::PJ_COORD.new(coords_ptr[i])
234
- result[i] = Coordinate.from_coord(pj_coord)
235
- end
236
- result
237
- end
238
-
239
- # Measure the internal consistency of a given transformation. The function performs n round trip
240
- # transformations starting in either the forward or reverse direction.
241
- #
242
- # @see https://proj.org/development/reference/functions.html#c.proj_roundtrip
243
- #
244
- # @param direction [PJ_DIRECTION] The starting direction of transformation
245
- # @param iterations [Integer] The number of roundtrip transformations
246
- # @param coordinate [Coordinate] The input coordinate
247
- #
248
- # @return [Float] The euclidean distance of the starting point coordinate and the last coordinate after n iterations back and forth.
249
- def roundtrip(direction, iterations, coordinate)
250
- Api.proj_roundtrip(self, direction, iterations, coordinate.pj_coord)
251
- end
252
-
253
- # Returns a new PJ object whose axis order is the one expected for visualization purposes
254
- #
255
- # @see https://proj.org/development/reference/functions.html#c.proj_normalize_for_visualization
256
- #
257
- # @return [CoordinateOperationMixin] A new CoordinateOperation or nil in case of error
258
- def normalize_for_visualization
259
- ptr = Api.proj_normalize_for_visualization(self.context, self)
260
- self.class.create_object(ptr, self.context)
261
- end
262
-
263
- # Return the operation used during the last invocation of Transformation#forward or Transformation#inverse
264
- #
265
- # @see }https://proj.org/development/reference/functions.html#c.proj_trans_get_last_used_operation proj_trans_get_last_used_operation
266
- #
267
- # @return [CoordinateOperationMixin] The last used operation
268
- def last_used_operation
269
- ptr = Api.proj_trans_get_last_used_operation(self)
270
- self.class.create_object(ptr, self.context)
271
- end
272
-
273
- # Return whether a coordinate operation has a "ballpark" transformation
274
- #
275
- # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_has_ballpark_transformation
276
- #
277
- # @return [Boolean]
278
- def ballpark_transformation?
279
- result = Api.proj_coordoperation_has_ballpark_transformation(Context.current, self)
280
- result == 1 ? true : false
281
- end
282
-
283
- # Returns the accuracy (in meters) of a coordinate operation.
284
- #
285
- # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_accuracy
286
- #
287
- # @return [Float] The accuracy, or a negative value if unknown or in case of error.
288
- def accuracy
289
- Api.proj_coordoperation_get_accuracy(self.context, self)
290
- end
291
-
292
- # Returns the number of grids used by a CoordinateOperation
293
- #
294
- # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_grid_used_count
295
- #
296
- # @return [Integer]
297
- def grid_count
298
- Api.proj_coordoperation_get_grid_used_count(self.context, self)
299
- end
300
-
301
- # Returns information about a Grid
302
- #
303
- # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_grid_used
304
- #
305
- # @param index [Integer] Grid index
306
- #
307
- # @return [Integer]
308
- def grid(index)
309
- out_short_name = FFI::MemoryPointer.new(:string)
310
- out_full_name = FFI::MemoryPointer.new(:string)
311
- out_package_name = FFI::MemoryPointer.new(:string)
312
- out_url = FFI::MemoryPointer.new(:string)
313
- out_direct_download = FFI::MemoryPointer.new(:int)
314
- out_open_license = FFI::MemoryPointer.new(:int)
315
- out_available = FFI::MemoryPointer.new(:int)
316
-
317
- result = Api.proj_coordoperation_get_grid_used(self.context, self, index,
318
- out_short_name, out_full_name, out_package_name,
319
- out_url, out_direct_download ,
320
- out_open_license, out_available)
321
-
322
- if result != 1
323
- Error.check_object(self)
324
- end
325
-
326
- name_ptr = out_short_name.read_pointer
327
- full_name_ptr = out_full_name.read_pointer
328
- package_name_ptr = out_package_name.read_pointer
329
- url_ptr = out_url.read_pointer
330
- downloadable_ptr = out_direct_download
331
- open_license_ptr = out_open_license
332
- available_ptr = out_available
333
-
334
- unless name_ptr.null?
335
- Grid.new(name_ptr.read_string_to_null, self.context,
336
- full_name: full_name_ptr.null? ? nil : full_name_ptr.read_string_to_null,
337
- package_name: package_name_ptr.null? ? nil : package_name_ptr.read_string_to_null,
338
- url: url_ptr.null? ? nil : url_ptr.read_string_to_null,
339
- downloadable: downloadable_ptr.null? ? nil : downloadable_ptr.read_int == 1 ? true : false,
340
- open_license: open_license_ptr.null? ? nil : open_license_ptr.read_int == 1 ? true : false,
341
- available: available_ptr.null? ? nil : available_ptr.read_int == 1 ? true : false)
342
- end
343
- end
344
-
345
- # Return the parameters of a Helmert transformation as WKT1 TOWGS84 values
346
- #
347
- # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_towgs84_values
348
- #
349
- # @param error_if_incompatible [Boolean] If true an exception is thrown if the coordinate operation is not compatible with a WKT1 TOWGS84 representation
350
- #
351
- # @return [Array<Float>]] Array of 7 numbers that represent translation, rotation and scale parameters.
352
- # Rotation and scale difference terms might be zero if the transformation only includes translation parameters
353
- def to_wgs84(error_if_incompatible = false)
354
- parameter_count = 7
355
- out_values = FFI::MemoryPointer.new(:double, parameter_count)
356
- Api.proj_coordoperation_get_towgs84_values(self.context, self, out_values, parameter_count, error_if_incompatible ? 1 : 0)
357
- out_values.read_array_of_double(parameter_count)
358
- end
359
-
360
- # Returns the number of steps in a concatenated operation
361
- #
362
- # @see https://proj.org/development/reference/functions.html#c.proj_concatoperation_get_step_count
363
- #
364
- # @return [Integer] The number of steps
365
- def step_count
366
- Api.proj_concatoperation_get_step_count(self.context, self)
367
- end
368
-
369
- # Returns a step of a concatenated operation
370
- #
371
- # @see https://proj.org/development/reference/functions.html#c.proj_concatoperation_get_step
372
- #
373
- # @param index [Integer] Index of the step
374
- #
375
- # @return [PjObject]
376
- def step(index)
377
- ptr = Api.proj_concatoperation_get_step(self.context, self, index)
378
- self.class.create_object(ptr, self.context)
379
- end
380
- end
381
- end
1
+ # encoding: UTF-8
2
+ require 'stringio'
3
+
4
+ module Proj
5
+ Param = Struct.new(:name, :auth_name, :code, :value, :value_string,
6
+ :unit_conv_factor, :unit_name, :unit_auth_name, :unit_code, :unit_category,
7
+ keyword_init: true)
8
+
9
+ # Coordinate Operations convert {Coordinate coordinates} to a new value. In Proj they are
10
+ # can either by {Conversion conversions} that do not exert a change in reference frame
11
+ # or {Transformation transformations} which do.
12
+ module CoordinateOperationMixin
13
+ # Returns if an operation expects input in radians
14
+ #
15
+ # @see https://proj.org/development/reference/functions.html#c.proj_angular_input
16
+ #
17
+ # @param direction [PjDirection] Direction of transformation
18
+ def angular_input?(direction)
19
+ result = Api.proj_angular_input(self, direction)
20
+ result == 1 ? true : false
21
+ end
22
+
23
+ # Check if an operation returns output in radians
24
+ #
25
+ # @see https://proj.org/development/reference/functions.html#c.proj_angular_output
26
+ #
27
+ # @param direction [PjDirection] Direction of transformation
28
+ def angular_output?(direction)
29
+ result = Api.proj_angular_output(self, direction)
30
+ result == 1 ? true : false
31
+ end
32
+
33
+ # Returns if an operation expects input in degrees
34
+ #
35
+ # @see https://proj.org/development/reference/functions.html#c.proj_degree_input
36
+ #
37
+ # @param direction [PjDirection] Direction of transformation
38
+ def degree_input?(direction)
39
+ result = Api.proj_degree_input(self, direction)
40
+ result == 1 ? true : false
41
+ end
42
+
43
+ # Check if an operation returns output in degrees
44
+ #
45
+ # @see https://proj.org/development/reference/functions.html#c.proj_degree_output
46
+ #
47
+ # @param direction [PjDirection] Direction of transformation
48
+ def degree_output?(direction)
49
+ result = Api.proj_degree_output(self, direction)
50
+ result == 1 ? true : false
51
+ end
52
+
53
+ # Return whether a coordinate operation can be instantiated as a PROJ pipeline, checking in particular that referenced grids are available.
54
+ #
55
+ # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_is_instantiable
56
+ #
57
+ # @return [Boolean]
58
+ def instantiable?
59
+ result = Api.proj_coordoperation_is_instantiable(self.context, self)
60
+ result == 1 ? true : false
61
+ end
62
+
63
+ # Returns a coordinate operation that represents the inverse operation of this operation
64
+ #
65
+ # @return [Conversion, Transformation] Returns nil on error
66
+ def create_inverse
67
+ ptr = Api.proj_coordoperation_create_inverse(self.context, self)
68
+ self.class.create_object(ptr, self.context)
69
+ end
70
+
71
+ # @!visibility private
72
+ def method_info
73
+ out_method_name = FFI::MemoryPointer.new(:string)
74
+ out_method_auth_name = FFI::MemoryPointer.new(:string)
75
+ out_method_code = FFI::MemoryPointer.new(:string)
76
+
77
+ result = Api.proj_coordoperation_get_method_info(self.context, self, out_method_name, out_method_auth_name, out_method_code)
78
+
79
+ if result != 1
80
+ Error.check_object(self)
81
+ end
82
+
83
+ {:method_name => out_method_name.read_pointer.read_string_to_null,
84
+ :method_auth_name => out_method_auth_name.read_pointer.read_string_to_null,
85
+ :method_code => out_method_code.read_pointer.read_string_to_null}
86
+ end
87
+
88
+ # Returns the operation name
89
+ #
90
+ # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_method_info
91
+ #
92
+ # @return [String]
93
+ def method_name
94
+ method_info[:method_name]
95
+ end
96
+
97
+ # Returns the operation authority name
98
+ #
99
+ # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_method_info
100
+ #
101
+ # @return [String]
102
+ def method_auth_name
103
+ method_info[:method_auth_name]
104
+ end
105
+
106
+ # Returns the operation code
107
+ #
108
+ # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_method_info
109
+ #
110
+ # @return [String]
111
+ def method_code
112
+ method_info[:method_code]
113
+ end
114
+
115
+ # Returns the number of parameters of a SingleOperation
116
+ #
117
+ # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_param_count
118
+ #
119
+ # @return [Integer]
120
+ def param_count
121
+ Api.proj_coordoperation_get_param_count(self.context, self)
122
+ end
123
+
124
+ # Returns the index of a parameter of a SingleOperation
125
+ #
126
+ # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_param_index
127
+ #
128
+ # @param name [String] Name of the parameter. Must not be nil
129
+ #
130
+ # @return [Integer] Index of the parameter or -1 in case of error.
131
+ def param_index(name)
132
+ Api.proj_coordoperation_get_param_index(self.context, self, name)
133
+ end
134
+
135
+ # Returns a parameter of a SingleOperation
136
+ #
137
+ # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_param
138
+ #
139
+ # @param index [Integer] Parameter index
140
+ #
141
+ # @return [Param]
142
+ def param(index)
143
+ out_name = FFI::MemoryPointer.new(:string)
144
+ out_auth_name = FFI::MemoryPointer.new(:string)
145
+ out_code = FFI::MemoryPointer.new(:string)
146
+ out_value = FFI::MemoryPointer.new(:double)
147
+ out_value_string = FFI::MemoryPointer.new(:string)
148
+ out_unit_conv_factor = FFI::MemoryPointer.new(:double)
149
+ out_unit_name = FFI::MemoryPointer.new(:string)
150
+ out_unit_auth_name = FFI::MemoryPointer.new(:string)
151
+ out_unit_code = FFI::MemoryPointer.new(:string)
152
+ out_unit_category = FFI::MemoryPointer.new(:string)
153
+
154
+ result = Api.proj_coordoperation_get_param(self.context, self, index,
155
+ out_name, out_auth_name, out_code,
156
+ out_value, out_value_string,
157
+ out_unit_conv_factor,
158
+ out_unit_name, out_unit_auth_name,out_unit_code,
159
+ out_unit_category)
160
+ if result != 1
161
+ Error.check_object(self)
162
+ end
163
+
164
+ name_ptr = out_name.read_pointer
165
+ auth_name_ptr = out_auth_name.read_pointer
166
+ code_ptr = out_code.read_pointer
167
+ value_string_ptr = out_value_string.read_pointer
168
+ unit_name_ptr = out_unit_name.read_pointer
169
+ unit_auth_name_ptr = out_unit_auth_name.read_pointer
170
+ unit_code_ptr = out_unit_code.read_pointer
171
+ unit_category_ptr = out_unit_category.read_pointer
172
+
173
+ Param.new(name: name_ptr.null? ? nil : name_ptr.read_string_to_null,
174
+ auth_name: auth_name_ptr.null? ? nil : auth_name_ptr.read_string_to_null,
175
+ code: code_ptr.null? ? nil : code_ptr.read_string_to_null,
176
+ value: out_value.null? ? nil : out_value.read_double,
177
+ value_string: value_string_ptr.null? ? nil : value_string_ptr.read_string_to_null,
178
+ unit_conv_factor: out_unit_conv_factor.null? ? nil : out_unit_conv_factor.read_double,
179
+ unit_name: unit_name_ptr.null? ? nil : unit_name_ptr.read_string_to_null,
180
+ unit_auth_name: unit_auth_name_ptr.null? ? nil : unit_auth_name_ptr.read_string_to_null,
181
+ unit_code: unit_code_ptr.null? ? nil : unit_code_ptr.read_string_to_null,
182
+ unit_category: unit_category_ptr.null? ? nil : unit_category_ptr.read_string_to_null)
183
+ end
184
+
185
+ # Transforms a {Coordinate} from the source {Crs} to the target {Crs}. Coordinates should be expressed in
186
+ # the units and axis order of the definition of the source CRS. The returned transformed coordinate will
187
+ # be in the units and axis order of the definition of the target CRS.
188
+ #
189
+ # @see https://proj.org/development/reference/functions.html#c.proj_trans
190
+ #
191
+ # @param coord [Coordinate]
192
+ #
193
+ # @return [Coordinate]
194
+ def forward(coord)
195
+ self.transform(coord, :PJ_FWD)
196
+ end
197
+
198
+ # Transforms a {Coordinate} from the target {Crs} to the source {Crs}. Coordinates should be expressed in
199
+ # the units and axis order of the definition of the source CRS. The returned transformed coordinate will
200
+ # be in the units and axis order of the definition of the target CRS.
201
+ #
202
+ # @see https://proj.org/development/reference/functions.html#c.proj_trans
203
+ #
204
+ # @param coord [Coordinate]
205
+ #
206
+ # @return [Coordinate]
207
+ def inverse(coord)
208
+ self.transform(coord, :PJ_INV)
209
+ end
210
+
211
+ # Transforms a {Coordinate} in the specified direction. See {CoordinateOperationMixin#forward forward} and
212
+ # {CoordinateOperationMixin#inverse inverse}
213
+ #
214
+ # @see https://proj.org/development/reference/functions.html#c.proj_trans
215
+ #
216
+ # @param direction [PjDirection] Direction of transformation (:PJ_FWD or :PJ_INV)
217
+ # @param coord [Coordinate]
218
+ #
219
+ # @return [Coordinate]
220
+ def transform(coord, direction)
221
+ struct = Api.proj_trans(self, direction, coord)
222
+ Coordinate.from_coord(struct)
223
+ end
224
+
225
+ # Transform boundary densifying the edges to account for nonlinear transformations along these edges
226
+ # and extracting the outermost bounds.
227
+ #
228
+ # @see https://proj.org/development/reference/functions.html#c.proj_trans_bounds
229
+ #
230
+ # @param bounds [Bounds] Bounding box in source CRS (target CRS if direction is inverse).
231
+ # @param direction [PjDirection] The direction of the transformation.
232
+ # @param densify_points [Integer] Recommended to use 21. This is the number of points to use to densify the bounding polygon in the transformation.
233
+ #
234
+ # @return [Bounds] Bounding box in target CRS (target CRS if direction is inverse).
235
+ def transform_bounds(bounds, direction, densify_points = 21)
236
+ out_xmin = FFI::MemoryPointer.new(:double)
237
+ out_ymin = FFI::MemoryPointer.new(:double)
238
+ out_xmax = FFI::MemoryPointer.new(:double)
239
+ out_ymax = FFI::MemoryPointer.new(:double)
240
+
241
+ result = Api.proj_trans_bounds(self.context, self, direction,
242
+ bounds.xmin, bounds.ymin, bounds.xmax, bounds.ymax,
243
+ out_xmin, out_ymin, out_xmax, out_ymax, densify_points)
244
+
245
+ unless result == 0
246
+ Error.check_object(self)
247
+ end
248
+
249
+ Bounds.new(out_xmin.read_double, out_ymin.read_double, out_xmax.read_double, out_ymax.read_double)
250
+ end
251
+
252
+ # Transform 3D boundary densifying the edges to account for nonlinear transformations
253
+ # and extracting the outermost bounds.
254
+ #
255
+ # @see https://proj.org/development/reference/functions.html#c.proj_trans_bounds_3D
256
+ #
257
+ # @param bounds [Bounds3d] Bounding box in source CRS (target CRS if direction is inverse).
258
+ # @param direction [PjDirection] The direction of the transformation.
259
+ # @param densify_points [Integer] Number of points to use to densify the bounding polygon in the transformation.
260
+ #
261
+ # @return [Bounds3d] Bounding box in target CRS (target CRS if direction is inverse).
262
+ def transform_bounds_3d(bounds, direction, densify_points = 21)
263
+ out_xmin = FFI::MemoryPointer.new(:double)
264
+ out_ymin = FFI::MemoryPointer.new(:double)
265
+ out_zmin = FFI::MemoryPointer.new(:double)
266
+ out_xmax = FFI::MemoryPointer.new(:double)
267
+ out_ymax = FFI::MemoryPointer.new(:double)
268
+ out_zmax = FFI::MemoryPointer.new(:double)
269
+
270
+ result = Api.proj_trans_bounds_3d(self.context, self, direction,
271
+ bounds.xmin, bounds.ymin, bounds.zmin,
272
+ bounds.xmax, bounds.ymax, bounds.zmax,
273
+ out_xmin, out_ymin, out_zmin,
274
+ out_xmax, out_ymax, out_zmax,
275
+ densify_points)
276
+
277
+ unless result == 0
278
+ Error.check_object(self)
279
+ end
280
+
281
+ Bounds3d.new(out_xmin.read_double, out_ymin.read_double, out_zmin.read_double,
282
+ out_xmax.read_double, out_ymax.read_double, out_zmax.read_double)
283
+ end
284
+
285
+ # Transforms an array of {Coordinate coordinates}. Individual points that fail to transform
286
+ # will have their components set to Infinity.
287
+ #
288
+ # @param coordinates [Array<Coordinate>] Coordinates to transform
289
+ # @param direction [PjDirection] The direction of the transformation
290
+ #
291
+ # @return [Array<Coordinate>] Array of transformed coordinates
292
+ def transform_array(coordinates, direction)
293
+ coords_ptr = FFI::MemoryPointer.new(Api::PjCoord, coordinates.size)
294
+ coordinates.each_with_index do |coordinate, i|
295
+ pj_coord = Api::PjCoord.new(coords_ptr[i])
296
+ pj_coord.to_ptr.__copy_from__(coordinate.to_ptr, Api::PjCoord.size)
297
+ end
298
+
299
+ int = Api.proj_trans_array(self, direction, coordinates.size, coords_ptr)
300
+ unless int == 0
301
+ Error.check_object(self)
302
+ end
303
+
304
+ result = Array.new(coordinates.size)
305
+ coordinates.size.times do |i|
306
+ pj_coord = Api::PjCoord.new(coords_ptr[i])
307
+ result[i] = Coordinate.from_coord(pj_coord)
308
+ end
309
+ result
310
+ end
311
+
312
+ # Measure the internal consistency of a given transformation. The function performs n round trip
313
+ # transformations starting in either the forward or reverse direction.
314
+ #
315
+ # @see https://proj.org/development/reference/functions.html#c.proj_roundtrip
316
+ #
317
+ # @param direction [PjDirection] The starting direction of transformation
318
+ # @param iterations [Integer] The number of roundtrip transformations
319
+ # @param coordinate [Coordinate] The input coordinate
320
+ #
321
+ # @return [Float] The euclidean distance of the starting point coordinate and the last coordinate after n iterations back and forth.
322
+ def roundtrip(direction, iterations, coordinate)
323
+ Api.proj_roundtrip(self, direction, iterations, coordinate.pj_coord)
324
+ end
325
+
326
+ # Returns a new PJ object whose axis order is the one expected for visualization purposes
327
+ #
328
+ # @see https://proj.org/development/reference/functions.html#c.proj_normalize_for_visualization
329
+ #
330
+ # @return [CoordinateOperationMixin] A new CoordinateOperation or nil in case of error
331
+ def normalize_for_visualization
332
+ ptr = Api.proj_normalize_for_visualization(self.context, self)
333
+ self.class.create_object(ptr, self.context)
334
+ end
335
+
336
+ # Return the operation used during the last invocation of Transformation#forward or Transformation#inverse
337
+ #
338
+ # @see }https://proj.org/development/reference/functions.html#c.proj_trans_get_last_used_operation proj_trans_get_last_used_operation
339
+ #
340
+ # @return [CoordinateOperationMixin] The last used operation
341
+ def last_used_operation
342
+ ptr = Api.proj_trans_get_last_used_operation(self)
343
+ self.class.create_object(ptr, self.context)
344
+ end
345
+
346
+ # Return whether a coordinate operation has a "ballpark" transformation
347
+ #
348
+ # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_has_ballpark_transformation
349
+ #
350
+ # @return [Boolean]
351
+ def ballpark_transformation?
352
+ result = Api.proj_coordoperation_has_ballpark_transformation(self.context, self)
353
+ result == 1 ? true : false
354
+ end
355
+
356
+ # Return whether a coordinate operation requires time as an input for each coordinate.
357
+ #
358
+ # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_requires_per_coordinate_input_time
359
+ #
360
+ # @return [Boolean]
361
+ def requires_per_coordinate_input_time?
362
+ result = Api.proj_coordoperation_requires_per_coordinate_input_time(self.context, self)
363
+ result == 1 ? true : false
364
+ end
365
+
366
+ # Returns the accuracy (in meters) of a coordinate operation.
367
+ #
368
+ # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_accuracy
369
+ #
370
+ # @return [Float] The accuracy, or a negative value if unknown or in case of error.
371
+ def accuracy
372
+ Api.proj_coordoperation_get_accuracy(self.context, self)
373
+ end
374
+
375
+ # Returns the number of grids used by a CoordinateOperation
376
+ #
377
+ # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_grid_used_count
378
+ #
379
+ # @return [Integer]
380
+ def grid_count
381
+ Api.proj_coordoperation_get_grid_used_count(self.context, self)
382
+ end
383
+
384
+ # Returns information about a Grid
385
+ #
386
+ # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_grid_used
387
+ #
388
+ # @param index [Integer] Grid index
389
+ #
390
+ # @return [Integer]
391
+ def grid(index)
392
+ out_short_name = FFI::MemoryPointer.new(:string)
393
+ out_full_name = FFI::MemoryPointer.new(:string)
394
+ out_package_name = FFI::MemoryPointer.new(:string)
395
+ out_url = FFI::MemoryPointer.new(:string)
396
+ out_direct_download = FFI::MemoryPointer.new(:int)
397
+ out_open_license = FFI::MemoryPointer.new(:int)
398
+ out_available = FFI::MemoryPointer.new(:int)
399
+
400
+ result = Api.proj_coordoperation_get_grid_used(self.context, self, index,
401
+ out_short_name, out_full_name, out_package_name,
402
+ out_url, out_direct_download ,
403
+ out_open_license, out_available)
404
+
405
+ if result != 1
406
+ Error.check_object(self)
407
+ end
408
+
409
+ name_ptr = out_short_name.read_pointer
410
+ full_name_ptr = out_full_name.read_pointer
411
+ package_name_ptr = out_package_name.read_pointer
412
+ url_ptr = out_url.read_pointer
413
+ downloadable_ptr = out_direct_download
414
+ open_license_ptr = out_open_license
415
+ available_ptr = out_available
416
+
417
+ unless name_ptr.null?
418
+ Grid.new(name_ptr.read_string_to_null, self.context,
419
+ full_name: full_name_ptr.null? ? nil : full_name_ptr.read_string_to_null,
420
+ package_name: package_name_ptr.null? ? nil : package_name_ptr.read_string_to_null,
421
+ url: url_ptr.null? ? nil : url_ptr.read_string_to_null,
422
+ downloadable: downloadable_ptr.null? ? nil : downloadable_ptr.read_int == 1 ? true : false,
423
+ open_license: open_license_ptr.null? ? nil : open_license_ptr.read_int == 1 ? true : false,
424
+ available: available_ptr.null? ? nil : available_ptr.read_int == 1 ? true : false)
425
+ end
426
+ end
427
+
428
+ # Return the parameters of a Helmert transformation as WKT1 TOWGS84 values
429
+ #
430
+ # @see https://proj.org/development/reference/functions.html#c.proj_coordoperation_get_towgs84_values
431
+ #
432
+ # @param error_if_incompatible [Boolean] If true an exception is thrown if the coordinate operation is not compatible with a WKT1 TOWGS84 representation
433
+ #
434
+ # @return [Array<Float>]] Array of 7 numbers that represent translation, rotation and scale parameters.
435
+ # Rotation and scale difference terms might be zero if the transformation only includes translation parameters
436
+ def to_wgs84(error_if_incompatible = false)
437
+ parameter_count = 7
438
+ out_values = FFI::MemoryPointer.new(:double, parameter_count)
439
+ Api.proj_coordoperation_get_towgs84_values(self.context, self, out_values, parameter_count, error_if_incompatible ? 1 : 0)
440
+ out_values.read_array_of_double(parameter_count)
441
+ end
442
+
443
+ # Returns the number of steps in a concatenated operation
444
+ #
445
+ # @see https://proj.org/development/reference/functions.html#c.proj_concatoperation_get_step_count
446
+ #
447
+ # @return [Integer] The number of steps
448
+ def step_count
449
+ Api.proj_concatoperation_get_step_count(self.context, self)
450
+ end
451
+
452
+ # Returns a step of a concatenated operation
453
+ #
454
+ # @see https://proj.org/development/reference/functions.html#c.proj_concatoperation_get_step
455
+ #
456
+ # @param index [Integer] Index of the step
457
+ #
458
+ # @return [PjObject]
459
+ def step(index)
460
+ ptr = Api.proj_concatoperation_get_step(self.context, self, index)
461
+ self.class.create_object(ptr, self.context)
462
+ end
463
+ end
464
+ end