proj4rb 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog +26 -15
  3. data/README.rdoc +82 -44
  4. data/Rakefile +27 -27
  5. data/lib/api/api.rb +96 -118
  6. data/lib/api/api_5_0.rb +331 -300
  7. data/lib/api/api_5_1.rb +6 -6
  8. data/lib/api/api_5_2.rb +4 -4
  9. data/lib/api/api_6_0.rb +116 -14
  10. data/lib/api/api_6_1.rb +4 -4
  11. data/lib/api/api_6_2.rb +9 -6
  12. data/lib/api/api_6_3.rb +6 -0
  13. data/lib/api/api_7_0.rb +68 -0
  14. data/lib/api/api_7_1.rb +73 -0
  15. data/lib/api/api_7_2.rb +14 -0
  16. data/lib/api/api_8_0.rb +6 -0
  17. data/lib/api/api_8_1.rb +24 -0
  18. data/lib/api/api_8_2.rb +6 -0
  19. data/lib/api/api_9_1.rb +7 -0
  20. data/lib/api/api_9_2.rb +9 -0
  21. data/lib/api/api_experimental.rb +196 -0
  22. data/lib/proj/area.rb +73 -32
  23. data/lib/proj/axis_info.rb +44 -0
  24. data/lib/proj/bounds.rb +13 -0
  25. data/lib/proj/context.rb +174 -28
  26. data/lib/proj/conversion.rb +92 -0
  27. data/lib/proj/coordinate.rb +281 -197
  28. data/lib/proj/coordinate_operation_mixin.rb +381 -0
  29. data/lib/proj/coordinate_system.rb +137 -0
  30. data/lib/proj/crs.rb +672 -204
  31. data/lib/proj/crs_info.rb +47 -0
  32. data/lib/proj/database.rb +305 -0
  33. data/lib/proj/datum.rb +32 -0
  34. data/lib/proj/datum_ensemble.rb +34 -0
  35. data/lib/proj/ellipsoid.rb +77 -41
  36. data/lib/proj/error.rb +62 -9
  37. data/lib/proj/file_api.rb +166 -0
  38. data/lib/proj/grid.rb +121 -0
  39. data/lib/proj/grid_cache.rb +64 -0
  40. data/lib/proj/grid_info.rb +19 -0
  41. data/lib/proj/network_api.rb +92 -0
  42. data/lib/proj/operation.rb +42 -42
  43. data/lib/proj/operation_factory_context.rb +136 -0
  44. data/lib/proj/parameter.rb +38 -0
  45. data/lib/proj/parameters.rb +106 -0
  46. data/lib/proj/pj_object.rb +670 -80
  47. data/lib/proj/pj_objects.rb +44 -0
  48. data/lib/proj/prime_meridian.rb +65 -39
  49. data/lib/proj/projection.rb +698 -207
  50. data/lib/proj/session.rb +46 -0
  51. data/lib/proj/strings.rb +32 -0
  52. data/lib/proj/transformation.rb +101 -60
  53. data/lib/proj/unit.rb +108 -53
  54. data/lib/proj.rb +110 -9
  55. data/proj4rb.gemspec +5 -5
  56. data/test/abstract_test.rb +23 -1
  57. data/test/context_test.rb +172 -82
  58. data/test/conversion_test.rb +368 -0
  59. data/test/coordinate_system_test.rb +144 -0
  60. data/test/crs_test.rb +770 -71
  61. data/test/database_test.rb +360 -0
  62. data/test/datum_ensemble_test.rb +65 -0
  63. data/test/datum_test.rb +55 -0
  64. data/test/ellipsoid_test.rb +64 -18
  65. data/test/file_api_test.rb +66 -0
  66. data/test/grid_cache_test.rb +72 -0
  67. data/test/grid_test.rb +141 -0
  68. data/test/network_api_test.rb +45 -0
  69. data/test/operation_factory_context_test.rb +201 -0
  70. data/test/parameters_test.rb +40 -0
  71. data/test/pj_object_test.rb +179 -0
  72. data/test/prime_meridian_test.rb +76 -0
  73. data/test/proj_test.rb +46 -4
  74. data/test/projection_test.rb +646 -222
  75. data/test/session_test.rb +78 -0
  76. data/test/transformation_test.rb +149 -7
  77. data/test/unit_test.rb +57 -28
  78. metadata +51 -13
  79. data/lib/api/api_4_9.rb +0 -31
  80. data/lib/proj/config.rb +0 -70
  81. data/lib/proj/point.rb +0 -72
  82. data/test/prime_meridians_test.rb +0 -33
data/test/context_test.rb CHANGED
@@ -1,82 +1,172 @@
1
- # encoding: UTF-8
2
-
3
- require_relative './abstract_test'
4
-
5
- class ContextTest < AbstractTest
6
- def test_create
7
- context = Proj::Context.new
8
- assert(context.to_ptr)
9
- end
10
-
11
- def test_finalize
12
- 500.times do
13
- context = Proj::Context.new
14
- assert(context.to_ptr)
15
- GC.start
16
- end
17
- assert(true)
18
- end
19
-
20
- def test_one_per_thread
21
- context_1 = Proj::Context.current
22
- context_2 = Proj::Context.current
23
- assert_same(context_1, context_2)
24
- end
25
-
26
- def test_database_path
27
- refute_nil(Proj::Context.current.database_path)
28
- end
29
-
30
- def test_log_level
31
- assert_equal(:PJ_LOG_NONE, Proj::Context.current.log_level)
32
- end
33
-
34
- def test_set_log_level
35
- context = Proj::Context.new
36
- context.log_level = :PJ_LOG_ERROR
37
- assert_equal(:PJ_LOG_ERROR, context.log_level)
38
- end
39
-
40
- def test_invalid_database_path
41
- path = '/wrong'
42
- error = assert_raises(Proj::Error) do
43
- Proj::Context.current.database_path = path
44
- end
45
- # TODO - if you run this test on its own you get a useful error message, if you run all tests
46
- # at once you get a useless error message. Not sure what is causing the difference
47
- assert_match(/No such file or directory|generic error of unknown origin/, error.to_s)
48
- end
49
-
50
- def test_set_log_function
51
- context = Proj::Context.new
52
- called = false
53
-
54
- data = FFI::MemoryPointer.new(:int)
55
- data.write_int(5)
56
-
57
- context.set_log_function(data) do |pointer, int, message|
58
- called = true
59
- refute(pointer.null?)
60
- assert_equal(5, pointer.read_int)
61
- assert_equal(1, int)
62
- assert_equal('proj_context_set_database_path: Open of /wrong failed', message)
63
- end
64
-
65
- begin
66
- context.database_path = '/wrong'
67
- rescue
68
- end
69
-
70
- assert(called)
71
- end
72
-
73
- def test_use_proj4_init_rules
74
- refute(Proj::Context.current.use_proj4_init_rules)
75
-
76
- Proj::Context.current.use_proj4_init_rules = true
77
- assert(Proj::Context.current.use_proj4_init_rules)
78
-
79
- Proj::Context.current.use_proj4_init_rules = false
80
- refute(Proj::Context.current.use_proj4_init_rules)
81
- end
82
- end
1
+ # encoding: UTF-8
2
+
3
+ require_relative './abstract_test'
4
+
5
+ class ContextTest < AbstractTest
6
+ def test_create
7
+ context = Proj::Context.new
8
+ assert(context.to_ptr)
9
+ end
10
+
11
+ def test_finalize
12
+ 100.times do
13
+ context = Proj::Context.new
14
+ assert(context.to_ptr)
15
+ GC.start
16
+ end
17
+ assert(true)
18
+ end
19
+
20
+ def test_clone
21
+ context = Proj::Context.new
22
+ refute(context.use_proj4_init_rules)
23
+ context.use_proj4_init_rules = true
24
+ assert(context.use_proj4_init_rules)
25
+
26
+ clone = context.clone
27
+ assert(clone.use_proj4_init_rules)
28
+ end
29
+
30
+ def test_dup
31
+ context = Proj::Context.new
32
+ refute(context.use_proj4_init_rules)
33
+ context.use_proj4_init_rules = true
34
+ assert(context.use_proj4_init_rules)
35
+
36
+ clone = context.clone
37
+ assert(clone.use_proj4_init_rules)
38
+ end
39
+
40
+ def test_one_per_thread
41
+ context_1 = Proj::Context.current
42
+ context_2 = Proj::Context.current
43
+ assert_same(context_1, context_2)
44
+ end
45
+
46
+ def test_search_paths
47
+ context = Proj::Context.new
48
+ path = File.join(Dir.tmpdir, "temp_proj_dic2")
49
+
50
+ begin
51
+ File.open(path, 'wb') do |file|
52
+ file << "<MY_PIPELINE> +proj=pipeline +step +proj=utm +zone=31 +ellps=GRS80"
53
+ end
54
+
55
+ # Try to use the pipeline, an error will occur since it is not on the path
56
+ error = assert_raises(Proj::Error) do
57
+ Proj::Conversion.new("+init=temp_proj_dic2:MY_PIPELINE")
58
+ end
59
+ assert_equal("Invalid value for an argument", error.to_s)
60
+
61
+ # Set the path and try again
62
+ context.search_paths = [File.dirname(path)]
63
+ conversion = Proj::Conversion.new("+init=temp_proj_dic2:MY_PIPELINE", context)
64
+ ensure
65
+ File.delete(path)
66
+ end
67
+ end
68
+
69
+ def test_database_path
70
+ refute_nil(Proj::Context.current.database_path)
71
+ end
72
+
73
+ def test_log_level
74
+ assert_equal(:PJ_LOG_ERROR, Proj::Context.current.log_level)
75
+ end
76
+
77
+ def test_set_log_level
78
+ context = Proj::Context.new
79
+ context.log_level = :PJ_LOG_ERROR
80
+ assert_equal(:PJ_LOG_ERROR, context.log_level)
81
+ end
82
+
83
+ def test_invalid_database_path
84
+ context = Proj::Context.new
85
+ path = '/wrong'
86
+ error = assert_raises(Proj::Error) do
87
+ context.database.path = path
88
+ end
89
+ # TODO - if you run this test on its own you get a useful error message, if you run all tests
90
+ # at once you get a useless error message. Not sure what is causing the difference
91
+ #assert_match(/No such file or directory|generic error of unknown origin|File not found or invalid/, error.to_s)
92
+ assert_equal("Unknown error (code 4096)", error.to_s)
93
+ end
94
+
95
+ def test_set_log_function
96
+ context = Proj::Context.new
97
+ called = false
98
+
99
+ data = FFI::MemoryPointer.new(:int)
100
+ data.write_int(5)
101
+
102
+ context.set_log_function(data) do |pointer, int, message|
103
+ called = true
104
+ refute(pointer.null?)
105
+ assert_equal(5, pointer.read_int)
106
+ assert_equal(1, int)
107
+ assert_equal('proj_context_set_database_path: Open of /wrong failed', message)
108
+ end
109
+
110
+ begin
111
+ context.database.path = '/wrong'
112
+ rescue
113
+ end
114
+
115
+ assert(called)
116
+ end
117
+
118
+ def test_use_proj4_init_rules
119
+ context = Proj::Context.new
120
+ refute(context.use_proj4_init_rules)
121
+
122
+ context.use_proj4_init_rules = true
123
+ assert(context.use_proj4_init_rules)
124
+
125
+ context.use_proj4_init_rules = false
126
+ refute(context.use_proj4_init_rules)
127
+ end
128
+
129
+ def test_network_enabled
130
+ context = Proj::Context.new
131
+ refute(context.network_enabled?)
132
+ end
133
+
134
+ def test_network_enabled_set
135
+ context = Proj::Context.new
136
+ refute(context.network_enabled?)
137
+
138
+ context.network_enabled = true
139
+ assert(context.network_enabled?)
140
+
141
+ context.network_enabled = false
142
+ refute(context.network_enabled?)
143
+ end
144
+
145
+ def test_url
146
+ context = Proj::Context.new
147
+ assert_equal("https://cdn.proj.org", context.url)
148
+ end
149
+
150
+ def test_url_set
151
+ context = Proj::Context.new
152
+ assert_equal("https://cdn.proj.org", context.url)
153
+
154
+ context.url = "https://cdn.proj.org/changed"
155
+ assert_equal("https://cdn.proj.org/changed", context.url)
156
+
157
+ context.url = "https://cdn.proj.org"
158
+ assert_equal("https://cdn.proj.org", context.url)
159
+ end
160
+
161
+ def test_user_directory
162
+ context = Proj::Context.new
163
+ assert_match(/proj$/, context.user_directory)
164
+ end
165
+
166
+ def test_wkt_dialect
167
+ context = Proj::Context.new
168
+
169
+ wkt = 'LOCAL_CS["foo"]'
170
+ assert_equal(:PJ_GUESSED_WKT2_2015, context.wkt_dialect(wkt))
171
+ end
172
+ end
@@ -0,0 +1,368 @@
1
+ # encoding: UTF-8
2
+
3
+ require_relative './abstract_test'
4
+
5
+ class ConversionTest < AbstractTest
6
+ def test_create_conversion
7
+ context = Proj::Context.new
8
+ param = Proj::Parameter.new(name: "param name", value: 0.99,
9
+ unit_conv_factor: 1.0, unit_type: :PJ_UT_SCALE)
10
+
11
+ conversion = Proj::Conversion.create_conversion(context, name: "conv",
12
+ auth_name: "conv auth", code: "conv code",
13
+ method_name: "method", method_auth_name: "method auth", method_code: "method code",
14
+ params: [param])
15
+ assert(conversion)
16
+ assert_equal("conv", conversion.name)
17
+ assert_equal("conv auth:conv code", conversion.auth)
18
+ assert_equal(:PJ_TYPE_CONVERSION, conversion.proj_type)
19
+ end
20
+
21
+ def test_inverse_operation
22
+ operation = Proj::Conversion.new(<<~EOS)
23
+ +proj=pipeline +step +proj=axisswap +order=2,1 +step
24
+ +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push
25
+ +v_3 +step +proj=cart +ellps=evrst30 +step +proj=helmert
26
+ +x=293 +y=836 +z=318 +rx=0.5 +ry=1.6 +rz=-2.8 +s=2.1
27
+ +convention=position_vector +step +inv +proj=cart
28
+ +ellps=WGS84 +step +proj=pop +v_3 +step +proj=unitconvert
29
+ +xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1
30
+ EOS
31
+
32
+ inverse = operation.create_inverse
33
+ proj_string = inverse.to_proj_string(:PJ_PROJ_5, multiline: true, indentation_width: 4, max_line_length: 40)
34
+
35
+ expected = if proj7?
36
+ <<~EOS
37
+ +proj=pipeline
38
+ +step +proj=axisswap +order=2,1
39
+ +step +proj=unitconvert +xy_in=deg
40
+ +xy_out=rad
41
+ +step +proj=push +v_3
42
+ +step +proj=cart +ellps=WGS84
43
+ +step +inv +proj=helmert +x=293
44
+ +y=836 +z=318 +rx=0.5 +ry=1.6
45
+ +rz=-2.8 +s=2.1
46
+ +convention=position_vector
47
+ +step +inv +proj=cart +ellps=evrst30
48
+ +step +proj=pop +v_3
49
+ +step +proj=unitconvert +xy_in=rad
50
+ +xy_out=deg
51
+ +step +proj=axisswap +order=2,1
52
+ EOS
53
+ else
54
+ "+proj=pipeline +step +proj=axisswap +order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 +step +proj=cart +ellps=WGS84 +step +inv +proj=helmert +x=293 +y=836 +z=318 +rx=0.5 +ry=1.6 +rz=-2.8 +s=2.1 +convention=position_vector +step +inv +proj=cart +ellps=evrst30 +step +proj=pop +v_3 +step +proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"
55
+ end
56
+
57
+ assert_equal(expected.strip, proj_string)
58
+ end
59
+
60
+ def test_accuracy_coordinate_operation
61
+ object = Proj::Conversion.create_from_database("EPSG", "1170", :PJ_CATEGORY_COORDINATE_OPERATION)
62
+ assert_equal(16.0, object.accuracy)
63
+ end
64
+
65
+ def test_roundrip
66
+ conversion = Proj::Conversion.new("+proj=cart +ellps=GRS80")
67
+ coord1 = Proj::Coordinate.new(lon: Proj.degrees_to_radians(12), lat: Proj.degrees_to_radians(55), z: 100)
68
+ coord2 = conversion.forward(coord1)
69
+
70
+ dist = conversion.roundtrip(:PJ_FWD, 10000, coord1)
71
+ dist += conversion.roundtrip(:PJ_INV, 10000, coord2)
72
+ assert(dist < 4e-9)
73
+ end
74
+
75
+ def test_accuracy_projection
76
+ conversion = Proj::Conversion.create("+proj=helmert")
77
+ assert_equal(-1.0, conversion.accuracy)
78
+ end
79
+
80
+ def test_method_info
81
+ crs = Proj::Crs.create_from_database("EPSG", "32631", :PJ_CATEGORY_CRS)
82
+ operation = crs.coordinate_operation
83
+ assert_equal("Transverse Mercator", operation.method_name)
84
+ assert_equal("EPSG", operation.method_auth_name)
85
+ assert_equal("9807", operation.method_code)
86
+ end
87
+
88
+ def test_ballpark_transformation
89
+ crs = Proj::Crs.create_from_database("EPSG", "32631", :PJ_CATEGORY_CRS)
90
+ operation = crs.coordinate_operation
91
+ refute(operation.ballpark_transformation?)
92
+ end
93
+
94
+ def test_param_count
95
+ crs = Proj::Crs.create_from_database("EPSG", "32631", :PJ_CATEGORY_CRS)
96
+ operation = crs.coordinate_operation
97
+ assert_equal(5, operation.param_count)
98
+ end
99
+
100
+ def test_param
101
+ crs = Proj::Crs.create_from_database("EPSG", "32631", :PJ_CATEGORY_CRS)
102
+ operation = crs.coordinate_operation
103
+
104
+ param = operation.param(3)
105
+ assert_equal("False easting", param.name)
106
+ assert_equal("EPSG", param.auth_name)
107
+ assert_equal("8806", param.code)
108
+ assert_equal(500000.0, param.value)
109
+ refute(param.value_string)
110
+ assert_equal(1.0, param.unit_conv_factor)
111
+ assert_equal("metre", param.unit_name)
112
+ assert_equal("EPSG", param.unit_auth_name)
113
+ assert_equal("9001", param.unit_code)
114
+ assert_equal("linear", param.unit_category)
115
+ end
116
+
117
+ def test_grid_count
118
+ operation = Proj::Conversion.create_from_database("EPSG", "1312", :PJ_CATEGORY_COORDINATE_OPERATION)
119
+ assert_equal(1, operation.grid_count)
120
+ end
121
+
122
+ def test_grid_url_invalid_index
123
+ context = Proj::Context.new
124
+ conversion = Proj::Conversion.create_from_database("EPSG", "1312", :PJ_CATEGORY_COORDINATE_OPERATION, false, context)
125
+
126
+ error = assert_raises(Proj::Error) do
127
+ conversion.grid(-1)
128
+ end
129
+
130
+ if proj9?
131
+ assert_equal("File not found or invalid", error.to_s)
132
+ else
133
+ assert_equal("Unknown error (code 4096)", error.to_s)
134
+ end
135
+ end
136
+
137
+ def test_grid_url
138
+ context = Proj::Context.new
139
+
140
+ conversion = Proj::Conversion.create_from_database("EPSG", "1312", :PJ_CATEGORY_COORDINATE_OPERATION, true, context)
141
+ grid = conversion.grid(0)
142
+
143
+ assert_equal("ca_nrc_ntv1_can.tif", grid.name)
144
+ assert_match(/ntv1_can/, grid.full_name)
145
+ assert(grid.package_name.empty?)
146
+ assert_equal("https://cdn.proj.org/ca_nrc_ntv1_can.tif", grid.url)
147
+ assert(grid.downloadable?)
148
+ assert(grid.open_license?)
149
+ assert(grid.available?)
150
+ end
151
+
152
+ def test_xy_dist
153
+ conversion = Proj::Conversion.new("+proj=utm; +zone=32; +ellps=GRS80")
154
+ coord1 = Proj::Coordinate.new(lam: Proj.degrees_to_radians(12),
155
+ phi: Proj.degrees_to_radians(55))
156
+
157
+ coord2 = conversion.forward(coord1)
158
+ coord1 = conversion.forward(coord1)
159
+
160
+ dist = conversion.xy_distance(coord1, coord2)
161
+ assert(dist < 2e-9)
162
+ end
163
+
164
+ def test_angular_input
165
+ conversion = Proj::Conversion.new("+proj=cart +ellps=GRS80")
166
+ assert(conversion.angular_input?(:PJ_FWD))
167
+ refute(conversion.angular_input?(:PJ_INV))
168
+ end
169
+
170
+ def test_angular_output
171
+ conversion = Proj::Conversion.new("+proj=cart +ellps=GRS80")
172
+ refute(conversion.angular_output?(:PJ_FWD))
173
+ assert(conversion.angular_output?(:PJ_INV))
174
+ end
175
+
176
+ def test_degree_input
177
+ skip "Unsure why these test fail"
178
+ conversion = Proj::Conversion.new(<<~EOS)
179
+ +proj=pipeline
180
+ +step +inv +proj=utm +zone=32 +ellps=GRS80
181
+ "+step +proj=unitconvert +xy_in=rad +xy_out=deg
182
+ EOS
183
+
184
+ refute(conversion.degree_input?(:PJ_FWD))
185
+ assert(conversion.degree_input?(:PJ_INV))
186
+ end
187
+
188
+ def test_degree_output
189
+ skip "Unsure why these test fail"
190
+ conversion = Proj::Conversion.new(<<~EOS)
191
+ +proj=pipeline
192
+ +step +inv +proj=utm +zone=32 +ellps=GRS80
193
+ "+step +proj=unitconvert +xy_in=rad +xy_out=deg
194
+ EOS
195
+
196
+ assert(conversion.degree_output?(:PJ_FWD))
197
+ refute(conversion.degree_output?(:PJ_INV))
198
+ end
199
+
200
+ def test_steps_concatenated
201
+ context = Proj::Context.new
202
+ source_crs = Proj::Conversion.create_from_database("EPSG", "28356", :PJ_CATEGORY_CRS)
203
+ target_crs = Proj::Conversion.create_from_database("EPSG", "7856", :PJ_CATEGORY_CRS)
204
+
205
+ factory_context = Proj::OperationFactoryContext.new(context)
206
+ factory_context.spatial_criterion = :PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION
207
+ factory_context.grid_availability = :PROJ_GRID_AVAILABILITY_IGNORED
208
+
209
+ operations = factory_context.create_operations(source_crs, target_crs)
210
+ assert_equal(3, operations.count)
211
+
212
+ operation = operations[0]
213
+ assert_instance_of(Proj::Conversion, operation)
214
+ assert_equal(:PJ_TYPE_CONCATENATED_OPERATION, operation.proj_type)
215
+
216
+ assert_equal(3, operation.step_count)
217
+ refute(operation.step(-1))
218
+
219
+ step = operation.step(1)
220
+ assert_equal("Transformation of GDA94 coordinates that have been derived through GNSS CORS.", step.scope)
221
+ assert_equal("Scale difference in ppb where 1/billion = 1E-9. See CT codes 8444-46 for NTv2 method giving equivalent results for Christmas Island, Cocos Islands and Australia respectively. See CT code 8447 for alternative including distortion model for Australia only.", step.remarks)
222
+ end
223
+
224
+ def test_transform_array
225
+ crs = Proj::Conversion.new("+proj=utm +zone=32 +ellps=GRS80")
226
+
227
+ coord1 = Proj::Coordinate.new(lon: Proj.degrees_to_radians(12), lat: Proj.degrees_to_radians(55), z: 45)
228
+ coord2 = Proj::Coordinate.new(lon: Proj.degrees_to_radians(12), lat: Proj.degrees_to_radians(56), z: 50)
229
+ new_coords = crs.transform_array([coord1, coord2], :PJ_FWD)
230
+
231
+ coord = new_coords[0]
232
+ assert_equal(691875.6321396607, coord.x)
233
+ assert_equal(6098907.825005012, coord.y)
234
+ assert_equal(45, coord.z)
235
+ assert_equal(0, coord.t)
236
+
237
+ coord = new_coords[1]
238
+ assert_equal(687071.439109443, coord.x)
239
+ assert_equal(6210141.326748009, coord.y)
240
+ assert_equal(50, coord.z)
241
+ assert_equal(0, coord.t)
242
+ end
243
+
244
+ def test_transform_array_invalid
245
+ context = Proj::Context.new
246
+ crs = Proj::Conversion.new("+proj=utm +zone=32 +ellps=GRS80", context)
247
+
248
+ coord1 = Proj::Coordinate.new(lon: Proj.degrees_to_radians(12), lat: Proj.degrees_to_radians(95), z: 45)
249
+ coord2 = Proj::Coordinate.new(lon: Proj.degrees_to_radians(12), lat: Proj.degrees_to_radians(56), z: 50)
250
+
251
+ error = assert_raises(Proj::Error) do
252
+ crs.transform_array([coord1, coord2], :PJ_FWD)
253
+ end
254
+
255
+ assert_equal("Invalid coordinate", error.to_s)
256
+ end
257
+
258
+ def test_pipeline
259
+ conversion = Proj::Conversion.new(<<~EOS)
260
+ +proj=pipeline
261
+ +step +inv +proj=lcc +lat_1=33.88333333333333
262
+ +lat_2=32.78333333333333 +lat_0=32.16666666666666
263
+ +lon_0=-116.25 +x_0=2000000.0001016 +y_0=500000.0001016001 +ellps=GRS80
264
+ +towgs84=0,0,0,0,0,0,0 +units=us-ft +no_defs
265
+ +step +proj=lcc +lat_1=33.88333333333333 +lat_2=32.78333333333333 +lat_0=32.16666666666666
266
+ +lon_0=-116.25 +x_0=2000000 +y_0=500000
267
+ +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs
268
+ EOS
269
+
270
+ # The Presidio, approximately
271
+ coordinate_1 = Proj::Coordinate.new(x: 4760096.421921, y: 3744293.729449)
272
+ coordinate_2 = conversion.forward(coordinate_1)
273
+
274
+ assert_in_delta(1450880.2910605003, coordinate_2.x)
275
+ assert_in_delta(1141263.01116045, coordinate_2.y)
276
+ end
277
+
278
+ if proj9?
279
+ def test_last_used_operation
280
+ wkt = <<~EOS
281
+ CONVERSION["UTM zone 31N",
282
+ METHOD["Transverse Mercator",
283
+ ID["EPSG",9807]],
284
+ PARAMETER["Latitude of natural origin",0,
285
+ ANGLEUNIT["degree",0.0174532925199433],
286
+ ID["EPSG",8801]],
287
+ PARAMETER["Longitude of natural origin",3,
288
+ ANGLEUNIT["degree",0.0174532925199433],
289
+ ID["EPSG",8802]],
290
+ PARAMETER["Scale factor at natural origin",0.9996,
291
+ SCALEUNIT["unity",1],
292
+ ID["EPSG",8805]],
293
+ PARAMETER["False easting",500000,
294
+ LENGTHUNIT["metre",1],
295
+ ID["EPSG",8806]],
296
+ PARAMETER["False northing",0,
297
+ LENGTHUNIT["metre",1],
298
+ ID["EPSG",8807]],
299
+ ID["EPSG",16031]]
300
+ EOS
301
+
302
+ operation = Proj::Conversion.create_from_wkt(wkt)
303
+ puts operation.to_wkt
304
+
305
+ operation = Proj::Conversion.create_from_database("EPSG", "16031", :PJ_CATEGORY_COORDINATE_OPERATION)
306
+ puts operation.to_wkt
307
+
308
+ last = operation.last_used_operation
309
+ refute(last)
310
+
311
+ coord = Proj::Coordinate.new(x: Proj::Api.proj_torad(3.0), y: 0, z: 0, t: 0)
312
+ new_coord = operation.forward(coord)
313
+
314
+ assert_in_delta(500000, new_coord.x, 1.0)
315
+ assert_in_delta(0.0, new_coord.y)
316
+ assert_in_delta(0.0, new_coord.z)
317
+ assert_in_delta(0.0, new_coord.t)
318
+
319
+ last = operation.last_used_operation
320
+ assert(last)
321
+ assert(last.equivalent_to?(operation, :PJ_COMP_STRICT))
322
+ end
323
+ end
324
+
325
+ def test_convert_conversion_to_other_method
326
+ context = Proj::Context.new
327
+ coordinate_system = Proj::CoordinateSystem.create_ellipsoidal_2d(:PJ_ELLPS2D_LONGITUDE_LATITUDE, context)
328
+
329
+ crs = Proj::Crs.create_geographic(context, name: "WGS 84", datum_name: "World Geodetic System 1984", ellps_name: "WGS 84",
330
+ semi_major_meter: 6378137, inv_flattening: 298.257223563,
331
+ prime_meridian_name: "Greenwich", prime_meridian_offset: 0.0, pm_angular_units: "Degree", pm_units_conv: 0.0174532925199433,
332
+ coordinate_system: coordinate_system)
333
+
334
+ mercator = Proj::Projection.mercator_variant_a(context, center_lat: 0, center_long: 1,
335
+ scale: 0.99,
336
+ false_easting: 2, false_northing: 3,
337
+ ang_unit_name: "Degree", ang_unit_conv_factor: 0.0174532925199433,
338
+ linear_unit_name: "Metre", linear_unit_conv_factor: 1.0)
339
+
340
+ cartesian = Proj::CoordinateSystem.create_cartesian_2d(context, :PJ_CART2D_EASTING_NORTHING)
341
+
342
+ projected = Proj::Crs.create_projected(context, name: "My CRS", geodetic_crs: crs,
343
+ conversion: mercator, coordinate_system: cartesian)
344
+
345
+ conversion = projected.coordinate_operation
346
+ assert_equal(:PJ_TYPE_CONVERSION, conversion.proj_type)
347
+
348
+ # Error - Don't set code or method name
349
+ new_conversion = conversion.convert_to_other_method
350
+ refute(new_conversion)
351
+
352
+ # 9805 is epsg code mercator variant b
353
+ new_conversion = conversion.convert_to_other_method(new_method_epsg_code: 9805)
354
+ assert(new_conversion)
355
+ refute(new_conversion.equivalent_to?(conversion, :PJ_COMP_STRICT))
356
+ assert(new_conversion.equivalent_to?(conversion, :PJ_COMP_EQUIVALENT))
357
+
358
+ new_conversion = conversion.convert_to_other_method(new_method_name: "Mercator (variant B)")
359
+ assert(new_conversion)
360
+ refute(new_conversion.equivalent_to?(conversion, :PJ_COMP_STRICT))
361
+ assert(new_conversion.equivalent_to?(conversion, :PJ_COMP_EQUIVALENT))
362
+
363
+ # Convert back
364
+ new_conversion = conversion.convert_to_other_method(new_method_name: "Mercator (variant A)")
365
+ assert(new_conversion)
366
+ assert(new_conversion.equivalent_to?(conversion, :PJ_COMP_STRICT))
367
+ end
368
+ end