proj4rb 3.0.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog +26 -15
  3. data/README.rdoc +82 -44
  4. data/Rakefile +27 -27
  5. data/lib/api/api.rb +96 -118
  6. data/lib/api/api_5_0.rb +331 -300
  7. data/lib/api/api_5_1.rb +6 -6
  8. data/lib/api/api_5_2.rb +4 -4
  9. data/lib/api/api_6_0.rb +116 -14
  10. data/lib/api/api_6_1.rb +4 -4
  11. data/lib/api/api_6_2.rb +9 -6
  12. data/lib/api/api_6_3.rb +6 -0
  13. data/lib/api/api_7_0.rb +68 -0
  14. data/lib/api/api_7_1.rb +73 -0
  15. data/lib/api/api_7_2.rb +14 -0
  16. data/lib/api/api_8_0.rb +6 -0
  17. data/lib/api/api_8_1.rb +24 -0
  18. data/lib/api/api_8_2.rb +6 -0
  19. data/lib/api/api_9_1.rb +7 -0
  20. data/lib/api/api_9_2.rb +9 -0
  21. data/lib/api/api_experimental.rb +196 -0
  22. data/lib/proj/area.rb +73 -32
  23. data/lib/proj/axis_info.rb +44 -0
  24. data/lib/proj/bounds.rb +13 -0
  25. data/lib/proj/context.rb +174 -28
  26. data/lib/proj/conversion.rb +92 -0
  27. data/lib/proj/coordinate.rb +281 -197
  28. data/lib/proj/coordinate_operation_mixin.rb +381 -0
  29. data/lib/proj/coordinate_system.rb +137 -0
  30. data/lib/proj/crs.rb +672 -204
  31. data/lib/proj/crs_info.rb +47 -0
  32. data/lib/proj/database.rb +305 -0
  33. data/lib/proj/datum.rb +32 -0
  34. data/lib/proj/datum_ensemble.rb +34 -0
  35. data/lib/proj/ellipsoid.rb +77 -41
  36. data/lib/proj/error.rb +62 -9
  37. data/lib/proj/file_api.rb +166 -0
  38. data/lib/proj/grid.rb +121 -0
  39. data/lib/proj/grid_cache.rb +64 -0
  40. data/lib/proj/grid_info.rb +19 -0
  41. data/lib/proj/network_api.rb +92 -0
  42. data/lib/proj/operation.rb +42 -42
  43. data/lib/proj/operation_factory_context.rb +136 -0
  44. data/lib/proj/parameter.rb +38 -0
  45. data/lib/proj/parameters.rb +106 -0
  46. data/lib/proj/pj_object.rb +670 -80
  47. data/lib/proj/pj_objects.rb +44 -0
  48. data/lib/proj/prime_meridian.rb +65 -39
  49. data/lib/proj/projection.rb +698 -207
  50. data/lib/proj/session.rb +46 -0
  51. data/lib/proj/strings.rb +32 -0
  52. data/lib/proj/transformation.rb +101 -60
  53. data/lib/proj/unit.rb +108 -53
  54. data/lib/proj.rb +110 -9
  55. data/proj4rb.gemspec +5 -5
  56. data/test/abstract_test.rb +23 -1
  57. data/test/context_test.rb +172 -82
  58. data/test/conversion_test.rb +368 -0
  59. data/test/coordinate_system_test.rb +144 -0
  60. data/test/crs_test.rb +770 -71
  61. data/test/database_test.rb +360 -0
  62. data/test/datum_ensemble_test.rb +65 -0
  63. data/test/datum_test.rb +55 -0
  64. data/test/ellipsoid_test.rb +64 -18
  65. data/test/file_api_test.rb +66 -0
  66. data/test/grid_cache_test.rb +72 -0
  67. data/test/grid_test.rb +141 -0
  68. data/test/network_api_test.rb +45 -0
  69. data/test/operation_factory_context_test.rb +201 -0
  70. data/test/parameters_test.rb +40 -0
  71. data/test/pj_object_test.rb +179 -0
  72. data/test/prime_meridian_test.rb +76 -0
  73. data/test/proj_test.rb +46 -4
  74. data/test/projection_test.rb +646 -222
  75. data/test/session_test.rb +78 -0
  76. data/test/transformation_test.rb +149 -7
  77. data/test/unit_test.rb +57 -28
  78. metadata +51 -13
  79. data/lib/api/api_4_9.rb +0 -31
  80. data/lib/proj/config.rb +0 -70
  81. data/lib/proj/point.rb +0 -72
  82. data/test/prime_meridians_test.rb +0 -33
@@ -0,0 +1,166 @@
1
+ module Proj
2
+ module FileApiCallbacks
3
+ def install_callbacks(context)
4
+ proj_file_api = Api::PROJ_FILE_API.new
5
+ proj_file_api[:version] = 1
6
+
7
+ # Store procs to instance variables so they don't get garbage collected
8
+ @open_cbk = proj_file_api[:open_cbk] = self.method(:open_callback)
9
+ @read_cbk = proj_file_api[:read_cbk] = self.method(:read_callback)
10
+ @write_cbk = proj_file_api[:write_cbk] = self.method(:write_callback)
11
+ @seek_cbk = proj_file_api[:seek_cbk] = self.method(:seek_callback)
12
+ @tell_cbk = proj_file_api[:tell_cbk] = self.method(:tell_callback)
13
+ @close_cbk = proj_file_api[:close_cbk] = self.method(:close_callback)
14
+ @exists_cbk = proj_file_api[:exists_cbk] = self.method(:exists_callback)
15
+ @mkdir_cbk = proj_file_api[:mkdir_cbk] = self.method(:mkdir_callback)
16
+ @unlink_cbk = proj_file_api[:unlink_cbk] = self.method(:unlink_callback)
17
+ @rename_cbk = proj_file_api[:rename_cbk] = self.method(:rename_callback)
18
+
19
+ result = Api.proj_context_set_fileapi(context, proj_file_api, nil)
20
+
21
+ if result != 1
22
+ Error.check_object(self)
23
+ end
24
+ end
25
+
26
+ # Open file. Return NULL if error
27
+ def open_callback(context, path, access_mode, user_data)
28
+ result = self.open(path, access_mode)
29
+ result ? FFI::MemoryPointer.new(:size_t) : nil
30
+ end
31
+
32
+ # Read sizeBytes into buffer from current position and return number of bytes read
33
+ def read_callback(context, handle, buffer, size_bytes, user_data)
34
+ data = self.read(size_bytes)
35
+ read_bytes = [size_bytes, data.size].min
36
+ buffer.write_bytes(data, 0, read_bytes)
37
+ read_bytes
38
+ end
39
+
40
+ # Write sizeBytes into buffer from current position and return number of bytes written
41
+ def write_callback(context, handle, buffer, size_bytes, user_data)
42
+ data = buffer.get_bytes(0, size_bytes)
43
+ self.write(data)
44
+ end
45
+
46
+ # Seek to offset using whence=SEEK_SET/SEEK_CUR/SEEK_END. Return TRUE in case of success
47
+ def seek_callback(context, handle, offset, whence, user_data)
48
+ self.seek(offset, whence)
49
+ return 1 # True
50
+ end
51
+
52
+ # Return current file position
53
+ def tell_callback(context, handle, user_data)
54
+ self.tell
55
+ end
56
+
57
+ # Close file
58
+ def close_callback(context, handle, user_data)
59
+ self.close
60
+ end
61
+
62
+ # Return TRUE if a file exists
63
+ def exists_callback(context, path, user_data)
64
+ if self.exists(path)
65
+ 1
66
+ else
67
+ 0
68
+ end
69
+ end
70
+
71
+ # Return TRUE if directory exists or could be created
72
+ def mkdir_callback(context, path, user_data)
73
+ if self.mdkir(path)
74
+ 1
75
+ else
76
+ 0
77
+ end
78
+ end
79
+
80
+ # Return TRUE if file could be removed
81
+ def unlink_callback(context, path, user_data)
82
+ if self.unlink(path)
83
+ 1
84
+ else
85
+ 0
86
+ end
87
+ end
88
+
89
+ # Return TRUE if file could be renamed
90
+ def rename_callback(context, original_path, new_path, user_data)
91
+ if self.rename(original_path, new_path)
92
+ 1
93
+ else
94
+ 0
95
+ end
96
+ end
97
+ end
98
+
99
+ # Proj allows its file api to be replaced by a custom implementation. This can be
100
+ # done by calling Context#set_file_api with a user defined Class that includes the
101
+ # FileApiCallbacks module and implements its required methods.
102
+ #
103
+ # The FileApiImpl class is a simple example file api implementation.
104
+ class FileApiImpl
105
+ include FileApiCallbacks
106
+
107
+ def initialize(context)
108
+ install_callbacks(context)
109
+ end
110
+
111
+ def open(path, access_mode)
112
+ case access_mode
113
+ when :PROJ_OPEN_ACCESS_READ_ONLY
114
+ if File.exist?(path)
115
+ @file = File.open(path, :mode => 'rb')
116
+ else
117
+ nil # False
118
+ end
119
+ when :PROJ_OPEN_ACCESS_READ_UPDATE
120
+ if File.exist?(path)
121
+ @file = File.open(path, :mode => 'r+b')
122
+ else
123
+ nil # False
124
+ end
125
+ when :PROJ_OPEN_ACCESS_CREATE
126
+ @file = File.open(path, :mode => 'wb')
127
+ end
128
+ end
129
+
130
+ def read(size_bytes)
131
+ @file.read(size_bytes)
132
+ end
133
+
134
+ def write(data)
135
+ @file.write(data)
136
+ end
137
+
138
+ def seek(offset, whence)
139
+ @file.seek(offset, whence)
140
+ end
141
+
142
+ def tell
143
+ @file.tell
144
+ end
145
+
146
+ def close
147
+ @file.close
148
+ end
149
+
150
+ def exists(path)
151
+ File.exist?(path)
152
+ end
153
+
154
+ def mkdir(path)
155
+ Dir.mkdir(path)
156
+ end
157
+
158
+ def unlink(path)
159
+ File.unlink(path) if File.exist?(path)
160
+ end
161
+
162
+ def rename(original_path, new_path)
163
+ File.rename(original_path, new_path)
164
+ end
165
+ end
166
+ end
data/lib/proj/grid.rb ADDED
@@ -0,0 +1,121 @@
1
+ module Proj
2
+ # Grids define models that are used to perform dimension shifts.
3
+ #
4
+ # Grid files can be quite large and may not be included with Proj depending on how
5
+ # it was packaged and any grid licensing requirements. Therefore, Proj has the ability
6
+ # to download grids on the fly if {Context#network_enabled? networking} is enabled.
7
+ #
8
+ # @see https://proj.org/community/rfc/rfc-4.html#rfc4
9
+ class Grid
10
+ # @!attribute [r] context
11
+ # @return [Context] The grid context
12
+ # @!attribute [r] name
13
+ # @return [String] The grid's name
14
+ # @!attribute [r] full_name
15
+ # @return [String] The grid's full name
16
+ # @!attribute [r] package_name
17
+ # @return [String] The grid's package name
18
+ # @!attribute [r] url
19
+ # @return [URI] A url that can be used to download the grid
20
+ attr_reader :context, :name, :full_name, :package_name, :url
21
+
22
+ def initialize(name, context = Context.default, full_name: nil, package_name: nil, url: nil,
23
+ downloadable: false, open_license: false, available: false)
24
+ @name = name
25
+ @context = context
26
+ @full_name = full_name
27
+ @package_name = package_name
28
+ @url = url
29
+ @downloadable = downloadable
30
+ @open_license = open_license
31
+ @available = available
32
+ end
33
+
34
+ # Returns whether the grid can be downloaded
35
+ #
36
+ # @return [Boolean]
37
+ def downloadable?
38
+ @downloadable
39
+ end
40
+
41
+ # Returns whether the grid is released with an open license
42
+ #
43
+ # @return [Boolean]
44
+ def open_license?
45
+ @open_license
46
+ end
47
+
48
+ # Returns whether the grid is available at runtime
49
+ #
50
+ # @return [Boolean]
51
+ def available?
52
+ @available
53
+ end
54
+
55
+ # Returns information about this grid
56
+ #
57
+ # See https://proj.org/development/reference/functions.html#c.proj_grid_info proj_grid_info
58
+ #
59
+ # @return [GridInfo]
60
+ def info
61
+ ptr = Api.proj_grid_info(self.name)
62
+ GridInfo.new(ptr)
63
+ end
64
+
65
+ # Returns if a grid is available in the PROJ user-writable directory.
66
+ # This method will only return true if Context#network_enabled? is true
67
+ #
68
+ # @see https://proj.org/development/reference/functions.html#c.proj_is_download_needed
69
+ #
70
+ # @param ignore_ttl [Boolean] If set to FALSE, PROJ will only check the recentness of an already downloaded file, if the delay between the last time it has been verified and the current time exceeds the TTL setting. This can save network accesses. If set to TRUE, PROJ will unconditionally check from the server the recentness of the file.
71
+ #
72
+ # @return [Boolean]
73
+ def downloaded?(ignore_ttl = false)
74
+ if self.context.network_enabled?
75
+ result = Api.proj_is_download_needed(self.context, self.url&.to_s || self.name, ignore_ttl ? 1 : 0)
76
+ result == 1 ? false : true
77
+ else
78
+ false
79
+ end
80
+ end
81
+
82
+ # Download a file in the PROJ user-writable directory if has not already been downlaoded.
83
+ # This function can only be used if networking is enabled
84
+ #
85
+ # @see https://proj.org/development/reference/functions.html#c.proj_download_file
86
+ #
87
+ # @param ignore_ttl [Boolean] If set to FALSE, PROJ will only check the recentness of an already downloaded file, if the delay between the last time it has been verified and the current time exceeds the TTL setting. This can save network accesses. If set to TRUE, PROJ will unconditionally check from the server the recentness of the file.
88
+ # @yieldparam percent [Double] The progress downloading the file in the range of 0 to 1
89
+ #
90
+ # @return [Boolean] True if the download was successful or unneeded. Otherwise false
91
+ def download(ignore_ttl = false)
92
+ callback = if block_given?
93
+ Proc.new do |context, percent, user_data|
94
+ result = yield percent
95
+ # Return 1 to tell Proj to keep downloading the file
96
+ result ? 1 : 0
97
+ end
98
+ end
99
+
100
+ result = Api.proj_download_file(self.context, self.url&.to_s || self.name, ignore_ttl ? 1 : 0, callback, nil)
101
+ result == 1 ? true : false
102
+ end
103
+
104
+ # Deletes the grid if it has been downloaded
105
+ def delete
106
+ if self.downloaded?
107
+ path = File.join(self.context.user_directory, self.name)
108
+ File.delete(path) if File.exist?(path)
109
+ end
110
+ end
111
+
112
+ # Returns the path to the grid if it has been downloaded
113
+ #
114
+ # @return [String]
115
+ def path
116
+ if self.downloaded?
117
+ File.join(self.context.user_directory, self.name)
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,64 @@
1
+ module Proj
2
+ # To avoid repeated network access, it is possible to enable a local cache of grids.
3
+ # Grid data is stored in a SQLite3 database, cache.db, that is by default stored
4
+ # stored in the PROJ user writable directory.
5
+ #
6
+ # The local cache is enabled by default with a size of 300MB. Cache settings can be overridden
7
+ # by this class, env variables or the proj.ini file
8
+ #
9
+ # @see https://proj.org/usage/network.html#caching
10
+ class GridCache
11
+ attr_reader :context
12
+
13
+ def initialize(context)
14
+ @context = context
15
+ end
16
+
17
+ # Enables or disables the grid cache
18
+ #
19
+ # @param value [Boolean]
20
+ #
21
+ # @see https://proj.org/development/reference/functions.html#c.proj_grid_cache_set_enable
22
+ def enabled=(value)
23
+ Api.proj_grid_cache_set_enable(self.context, value ? 1 : 0)
24
+ end
25
+
26
+ # Set the path and file of the local cache file which is sqlite database. By default
27
+ # it is stored in the user writable directory.
28
+ #
29
+ # @param value [String] - Full path to the cache. If set to nil then caching will be disabled.
30
+ #
31
+ # @see https://proj.org/development/reference/functions.html#c.proj_grid_cache_set_filename
32
+ def path=(value)
33
+ Api.proj_grid_cache_set_filename(self.context, value.encode('UTF-8'))
34
+ value
35
+ end
36
+
37
+ # Sets the cache size
38
+ #
39
+ # @param value [Integer] Maximum size in Megabytes (1024*1024 bytes), or negative value to set unlimited size.
40
+ #
41
+ # @see https://proj.org/development/reference/functions.html#c.proj_grid_cache_set_max_size
42
+ def max_size=(value)
43
+ Api.proj_grid_cache_set_max_size(self.context, value)
44
+ value
45
+ end
46
+
47
+ # Specifies the time-to-live delay for re-checking if the cached properties of files are still up-to-date.
48
+ #
49
+ # @param value [Integer] Delay in seconds. Use negative value for no expiration.
50
+ #
51
+ # @see https://proj.org/development/reference/functions.html#c.proj_grid_cache_set_ttl
52
+ def ttl=(value)
53
+ Api.proj_grid_cache_set_ttl(self.context, value)
54
+ value
55
+ end
56
+
57
+ # Clears the cache
58
+ #
59
+ # @see https://proj.org/development/reference/functions.html#c.proj_grid_cache_clear
60
+ def clear
61
+ Api.proj_grid_cache_clear(self.context)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,19 @@
1
+ module Proj
2
+ class GridInfo
3
+ attr_reader :gridname, :filename, :format,
4
+ :lower_left, :upper_right,
5
+ :size_lon, :size_lat, :cell_size_lon, :cell_size_lat
6
+
7
+ def initialize(pj_grid_info)
8
+ @filename = pj_grid_info[:filename].to_ptr.read_string
9
+ @gridname = pj_grid_info[:gridname].to_ptr.read_string
10
+ @format = pj_grid_info[:format].to_ptr.read_string
11
+ @lower_left = pj_grid_info[:lowerleft]
12
+ @upper_right = pj_grid_info[:upperright]
13
+ @size_lon = pj_grid_info[:n_lon]
14
+ @size_lat = pj_grid_info[:n_lat]
15
+ @cell_size_lon = pj_grid_info[:cs_lon]
16
+ @cell_size_lat = pj_grid_info[:cs_lat]
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,92 @@
1
+ require 'net/http'
2
+
3
+ module Proj
4
+ module NetworkApiCallbacks
5
+ def install_callbacks(context)
6
+ @open_cbk = self.method(:open_callback)
7
+ @close_cbk = self.method(:close_callback)
8
+ @header_value_cbk = self.method(:header_value_callback)
9
+ @read_range_cbk = self.method(:read_range_callback)
10
+
11
+ result = Api.proj_context_set_network_callbacks(context, @open_cbk, @close_cbk, @header_value_cbk, @read_range_cbk, nil)
12
+
13
+ if result != 1
14
+ Error.check_object(self)
15
+ end
16
+ end
17
+
18
+ def open_callback(context, url, offset, size_to_read, buffer, out_size_read, error_string_max_size, out_error_string, user_data)
19
+ uri = URI.parse(url)
20
+ data = self.open(uri, offset, size_to_read)
21
+ out_size = [size_to_read, data.size].min
22
+ out_size_read.write(:size_t, out_size)
23
+ buffer.write_bytes(data, 0, out_size)
24
+
25
+ # Return fake handle
26
+ FFI::MemoryPointer.new(:size_t)
27
+ end
28
+
29
+ def close_callback(context, handle, user_data)
30
+ self.close
31
+ end
32
+
33
+ def header_value_callback(context, handle, header_name_ptr, user_data)
34
+ header_name = header_name_ptr.read_string_to_null
35
+ value = self.header_value(header_name)
36
+ FFI::MemoryPointer.from_string(value)
37
+ end
38
+
39
+ def read_range_callback(context, handle, offset, size_to_read, buffer, error_string_max_size, out_error_string, user_data)
40
+ data = self.read_range(offset, size_to_read)
41
+ out_size = [size_to_read, data.size].min
42
+ buffer.write_bytes(data, 0, out_size)
43
+ out_size
44
+ end
45
+ end
46
+
47
+ # Proj allows its network api to be replaced by a custom implementation. This can be
48
+ # done by calling Context#set_network_api with a user defined Class that includes the
49
+ # NetworkApiCallbacks module and implements its required methods.
50
+ #
51
+ # @see https://proj.org/usage/network.html
52
+ #
53
+ # The NetworkApiImpl class is a simple example of a network api implementation.
54
+ class NetworkApiImpl
55
+ include NetworkApiCallbacks
56
+
57
+ def initialize(context)
58
+ install_callbacks(context)
59
+ end
60
+
61
+ def open(uri, offset, size_to_read)
62
+ @uri = uri
63
+ @http = Net::HTTP.new(@uri.host, @uri.port)
64
+ if uri.scheme == "https"
65
+ @http.use_ssl = true
66
+ @http.verify_mode = OpenSSL::SSL::VERIFY_PEER
67
+ end
68
+ @http.start
69
+
70
+ read_data(offset, size_to_read)
71
+ end
72
+
73
+ def close
74
+ @http.finish
75
+ end
76
+
77
+ def header_value(name)
78
+ @response[name]
79
+ end
80
+
81
+ def read_range(offset, size_to_read)
82
+ read_data(offset, size_to_read)
83
+ end
84
+
85
+ def read_data(offset, size_to_read)
86
+ headers = {"Range": "bytes=#{offset}-#{offset + size_to_read - 1}"}
87
+ request = Net::HTTP::Get.new(@uri.request_uri, headers)
88
+ @response = @http.request(request)
89
+ @response.body.force_encoding("ASCII-8BIT")
90
+ end
91
+ end
92
+ end
@@ -1,43 +1,43 @@
1
- module Proj
2
- class Operation
3
- attr_reader :id, :description
4
-
5
- def self.list
6
- pointer_to_array = FFI::Pointer.new(Api::PJ_OPERATIONS, Api.proj_list_operations)
7
- result = Array.new
8
- 0.step do |i|
9
- operation_info = Api::PJ_OPERATIONS.new(pointer_to_array[i])
10
- break result if operation_info[:id].nil?
11
- id = operation_info[:id]
12
- description = operation_info[:descr].read_pointer.read_string.force_encoding('UTF-8')
13
- result << self.new(id, description)
14
- end
15
- result
16
- end
17
-
18
- def self.get(id)
19
- self.list.find {|operation| operation.id == id}
20
- end
21
-
22
- def initialize(id, description)
23
- @id = id
24
- @description = description
25
- end
26
-
27
- def <=>(other)
28
- self.id <=> other.id
29
- end
30
-
31
- def ==(other)
32
- self.id == other.id
33
- end
34
-
35
- def to_s
36
- self.id
37
- end
38
-
39
- def inspect
40
- "#<#{self.class} id=\"#{id}\", major=\"#{major}\", ell=\"#{ell}\", name=\"#{name}\">"
41
- end
42
- end
1
+ module Proj
2
+ class Operation
3
+ attr_reader :id, :description
4
+
5
+ def self.list
6
+ pointer_to_array = FFI::Pointer.new(Api::PJ_OPERATIONS, Api.proj_list_operations)
7
+ result = Array.new
8
+ 0.step do |i|
9
+ operation_info = Api::PJ_OPERATIONS.new(pointer_to_array[i])
10
+ break result if operation_info[:id].nil?
11
+ id = operation_info[:id]
12
+ description = operation_info[:descr].read_pointer.read_string.force_encoding('UTF-8')
13
+ result << self.new(id, description)
14
+ end
15
+ result
16
+ end
17
+
18
+ def self.get(id)
19
+ self.list.find {|operation| operation.id == id}
20
+ end
21
+
22
+ def initialize(id, description)
23
+ @id = id
24
+ @description = description
25
+ end
26
+
27
+ def <=>(other)
28
+ self.id <=> other.id
29
+ end
30
+
31
+ def ==(other)
32
+ self.id == other.id
33
+ end
34
+
35
+ def to_s
36
+ self.id
37
+ end
38
+
39
+ def inspect
40
+ "#<#{self.class} id=\"#{id}\", major=\"#{major}\", ell=\"#{ell}\", name=\"#{name}\">"
41
+ end
42
+ end
43
43
  end
@@ -0,0 +1,136 @@
1
+ module Proj
2
+ # A context for building coordinate operations between two CRS.
3
+ class OperationFactoryContext
4
+ attr_reader :context
5
+
6
+ def self.finalize(pointer)
7
+ proc do
8
+ Api.proj_operation_factory_context_destroy(pointer)
9
+ end
10
+ end
11
+
12
+ # Create a new OperationFactoryContext
13
+ #
14
+ # @param context - The context to use or the current context if nil
15
+ # @param authority - If authority is nil or the empty string, then coordinate operations
16
+ # from any authority will be searched, with the restrictions set
17
+ # in the authority_to_authority_preference database table.
18
+ # If authority is set to "any", then coordinate operations from any
19
+ # authority will be searched
20
+ # If authority is a non-empty string different of "any", then coordinate
21
+ # operations will be searched only in that authority namespace.
22
+ def initialize(context, authority: nil)
23
+ @pointer = Api.proj_create_operation_factory_context(context, authority)
24
+ ObjectSpace.define_finalizer(self, self.class.finalize(@pointer))
25
+ end
26
+
27
+ # Find a list of CoordinateOperation from source_crs to target_crs
28
+ #
29
+ # @param source [Crs] Source CRS. Must not be nil.
30
+ # @param target [Crs] Target CRS. Must not be nil.
31
+ #
32
+ # @return [Array] - Returns a list of operations
33
+ def create_operations(source, target)
34
+ ptr = Api.proj_create_operations(self.context, source, target, self)
35
+ PjObjects.new(ptr, self.context)
36
+ end
37
+
38
+ # Specifies whether ballpark transformations are allowed.
39
+ #
40
+ # @param value - Set to True allow ballpark transformations otherwise False
41
+ def ballpark_transformations=(value)
42
+ Api.proj_operation_factory_context_set_allow_ballpark_transformations(self.context, self, value ? 1 : 0)
43
+ end
44
+
45
+ # Set the desired accuracy of the resulting coordinate transformations.
46
+ #
47
+ # @param value [double] - Accuracy in meters. Set to 0 to disable the filter.
48
+ def desired_accuracy=(value)
49
+ Api.proj_operation_factory_context_set_desired_accuracy(self.context, self, value)
50
+ end
51
+
52
+ # Set the desired area of interest for the resulting coordinate transformations. For an
53
+ # area of interest crossing the anti-meridian, west_lon_degree will be greater than east_lon_degree.
54
+ #
55
+ # @param west West longitude (in degrees).
56
+ # @param south South latitude (in degrees).
57
+ # @param east East longitude (in degrees).
58
+ # @param north North latitude (in degrees).
59
+ def set_area_of_interest(west, south, east, north)
60
+ Api.proj_operation_factory_context_set_area_of_interest(self.context, self, west, south, east, north)
61
+ end
62
+
63
+ # Set the name of the desired area of interest for the resulting coordinate transformations.
64
+ #
65
+ # @param value - Name of the area. Must be known of the database.
66
+ def area_of_interest_name=(value)
67
+ Api.proj_operation_factory_context_set_area_of_interest_name(self.context, self, value)
68
+ end
69
+
70
+ # Set how source and target CRS extent should be used when considering if a transformation
71
+ # can be used (only takes effect if no area of interest is explicitly defined).
72
+ #
73
+ # @param value [PROJ_CRS_EXTENT_USE] How source and target CRS extent should be used.
74
+ def crs_extent_use=(value)
75
+ Api.proj_operation_factory_context_set_crs_extent_use(self.context, self, value)
76
+ end
77
+
78
+ # Set the spatial criterion to use when comparing the area of validity of coordinate operations
79
+ # with the area of interest / area of validity of source and target CRS.
80
+ #
81
+ # @param value [PROJ_SPATIAL_CRITERION] spatial criterion to use
82
+ def spatial_criterion=(value)
83
+ Api.proj_operation_factory_context_set_spatial_criterion(self.context, self, value)
84
+ end
85
+
86
+ # Set how grid availability is used.
87
+ #
88
+ # @param [PROJ_GRID_AVAILABILITY_USE] - Use how grid availability is used.
89
+ def grid_availability=(value)
90
+ Api.proj_operation_factory_context_set_grid_availability_use(self.context, self, value)
91
+ end
92
+
93
+ # Set whether PROJ alternative grid names should be substituted to the official authority names.
94
+ #
95
+ # @param value [boolean] - Whether PROJ alternative grid names should be used
96
+ def use_proj_alternative_grid_names=(value)
97
+ Api.proj_operation_factory_context_set_use_proj_alternative_grid_names(self.context, self, value ? 1 : 0)
98
+ end
99
+
100
+ # Set whether an intermediate pivot CRS can be used for researching coordinate operations
101
+ # between a source and target CRS.
102
+ #
103
+ # @param value [PROJ_INTERMEDIATE_CRS_USE] - Whether and how intermediate CRS may be used
104
+ def allow_use_intermediate_crs=(value)
105
+ Api.proj_operation_factory_context_set_allow_use_intermediate_crs(self.context, self, value)
106
+ end
107
+
108
+ # Restrict the potential pivot CRSs that can be used when trying to build a coordinate operation
109
+ # between two CRS that have no direct operation.
110
+ #
111
+ # @param values [Array] - Array of string with the format ["auth_name1", "code1", "auth_name2", "code2"]
112
+ def allowed_intermediate_crs=(values)
113
+ # Convert strings to C chars
114
+ values_ptr = values.map do |value|
115
+ FFI::MemoryPointer.from_string(value)
116
+ end
117
+
118
+ # Add extra item at end for null pointer
119
+ pointer = FFI::MemoryPointer.new(:pointer, values.size + 1)
120
+ pointer.write_array_of_pointer(values_ptr)
121
+
122
+ Api.proj_operation_factory_context_set_allowed_intermediate_crs(self.context, self, pointer)
123
+ end
124
+
125
+ # Set whether transformations that are superseded (but not deprecated) should be discarded.
126
+ #
127
+ # @param value [bool] - Whether to discard superseded crses
128
+ def discard_superseded=(value)
129
+ Api.proj_operation_factory_context_set_discard_superseded(self.context, self, value ? 1 : 0)
130
+ end
131
+
132
+ def to_ptr
133
+ @pointer
134
+ end
135
+ end
136
+ end