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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +98 -0
- data/Gemfile +4 -4
- data/README.md +53 -0
- data/lib/api/proj.rb +750 -0
- data/lib/api/proj_experimental.rb +7 -0
- data/lib/api/proj_ffi.rb +47 -0
- data/lib/api/proj_version.rb +26 -0
- data/lib/examples/axis_order_normalization.rb +13 -0
- data/lib/examples/batch_transformation.rb +25 -0
- data/lib/examples/context_logging.rb +26 -0
- data/lib/examples/crs_identification.rb +18 -0
- data/lib/examples/database_query.rb +27 -0
- data/lib/examples/geodetic_distance.rb +38 -0
- data/lib/examples/geodetic_to_projected.rb +18 -0
- data/lib/examples/operation_factory_context.rb +19 -0
- data/lib/examples/pipeline_operator.rb +21 -0
- data/lib/examples/promote_demote_3d.rb +23 -0
- data/lib/examples/serialization_formats.rb +17 -0
- data/lib/examples/transform_bounds.rb +18 -0
- data/lib/examples/transformation_with_area.rb +18 -0
- data/lib/proj/area.rb +74 -74
- data/lib/proj/axis_info.rb +44 -44
- data/lib/proj/bounds.rb +22 -0
- data/lib/proj/bounds3d.rb +45 -0
- data/lib/proj/context.rb +57 -23
- data/lib/proj/conversion.rb +94 -91
- data/lib/proj/coordinate.rb +304 -281
- data/lib/proj/coordinate_metadata.rb +38 -0
- data/lib/proj/coordinate_operation_mixin.rb +464 -381
- data/lib/proj/coordinate_system.rb +143 -137
- data/lib/proj/crs.rb +688 -672
- data/lib/proj/crs_info.rb +47 -47
- data/lib/proj/database.rb +310 -305
- data/lib/proj/datum.rb +32 -32
- data/lib/proj/datum_ensemble.rb +34 -34
- data/lib/proj/domain.rb +82 -0
- data/lib/proj/ellipsoid.rb +77 -77
- data/lib/proj/error.rb +7 -8
- data/lib/proj/file_api_callbacks.rb +165 -0
- data/lib/proj/grid.rb +121 -121
- data/lib/proj/grid_cache.rb +65 -64
- data/lib/proj/grid_info.rb +19 -19
- data/lib/proj/life_span.rb +21 -0
- data/lib/proj/network_api_callbacks.rb +86 -0
- data/lib/proj/operation.rb +66 -42
- data/lib/proj/operation_factory_context.rb +4 -2
- data/lib/proj/options.rb +41 -0
- data/lib/proj/parameter.rb +37 -37
- data/lib/proj/parameters.rb +106 -107
- data/lib/proj/pj_axis_description.rb +26 -0
- data/lib/proj/pj_object.rb +602 -670
- data/lib/proj/pj_objects.rb +45 -45
- data/lib/proj/pj_param_description.rb +28 -0
- data/lib/proj/prime_meridian.rb +65 -65
- data/lib/proj/projection.rb +1771 -698
- data/lib/proj/session.rb +2 -0
- data/lib/proj/transformation.rb +102 -102
- data/lib/proj/unit.rb +81 -108
- data/lib/proj.rb +10 -3
- data/lib/proj4.rb +5 -5
- data/proj4rb.gemspec +10 -5
- data/test/abstract_test.rb +7 -28
- data/test/context_test.rb +210 -172
- data/test/context_validation_test.rb +11 -0
- data/test/conversion_test.rb +376 -368
- data/test/coordinate_metadata_test.rb +34 -0
- data/test/coordinate_system_test.rb +162 -144
- data/test/coordinate_test.rb +289 -34
- data/test/crs_test.rb +1112 -1072
- data/test/database_test.rb +407 -359
- data/test/datum_ensemble_test.rb +64 -64
- data/test/datum_test.rb +61 -54
- data/test/domain_test.rb +72 -0
- data/test/ellipsoid_test.rb +80 -80
- data/test/examples_test.rb +149 -0
- data/test/file_api_example.rb +58 -0
- data/test/file_api_test.rb +74 -66
- data/test/grid_cache_test.rb +72 -72
- data/test/grid_test.rb +126 -141
- data/test/network_api_example.rb +48 -0
- data/test/network_api_test.rb +33 -45
- data/test/operation_factory_context_test.rb +225 -201
- data/test/operation_test.rb +40 -29
- data/test/options_test.rb +17 -0
- data/test/parameters_test.rb +86 -40
- data/test/pj_object_test.rb +221 -179
- data/test/prime_meridian_test.rb +75 -75
- data/test/proj_test.rb +58 -58
- data/test/projection_test.rb +680 -650
- data/test/session_test.rb +78 -77
- data/test/transformation_test.rb +238 -210
- data/test/unit_test.rb +114 -76
- metadata +45 -31
- data/ChangeLog +0 -89
- data/README.rdoc +0 -207
- data/lib/api/api.rb +0 -117
- data/lib/api/api_5_0.rb +0 -338
- data/lib/api/api_5_1.rb +0 -7
- data/lib/api/api_5_2.rb +0 -5
- data/lib/api/api_6_0.rb +0 -146
- data/lib/api/api_6_1.rb +0 -5
- data/lib/api/api_6_2.rb +0 -10
- data/lib/api/api_6_3.rb +0 -6
- data/lib/api/api_7_0.rb +0 -69
- data/lib/api/api_7_1.rb +0 -73
- data/lib/api/api_7_2.rb +0 -14
- data/lib/api/api_8_0.rb +0 -6
- data/lib/api/api_8_1.rb +0 -24
- data/lib/api/api_8_2.rb +0 -6
- data/lib/api/api_9_1.rb +0 -7
- data/lib/api/api_9_2.rb +0 -9
- data/lib/api/api_experimental.rb +0 -201
- data/lib/proj/file_api.rb +0 -166
- data/lib/proj/network_api.rb +0 -92
data/lib/proj/grid.rb
CHANGED
|
@@ -1,121 +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 [Float] 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
|
|
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 [Float] 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
|
data/lib/proj/grid_cache.rb
CHANGED
|
@@ -1,64 +1,65 @@
|
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
value
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
value
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
value
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
end
|
|
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
|
+
Error.validate_context!(context)
|
|
15
|
+
@context = context
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Enables or disables the grid cache
|
|
19
|
+
#
|
|
20
|
+
# @param value [Boolean]
|
|
21
|
+
#
|
|
22
|
+
# @see https://proj.org/development/reference/functions.html#c.proj_grid_cache_set_enable
|
|
23
|
+
def enabled=(value)
|
|
24
|
+
Api.proj_grid_cache_set_enable(self.context, value ? 1 : 0)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Set the path and file of the local cache file which is sqlite database. By default
|
|
28
|
+
# it is stored in the user writable directory.
|
|
29
|
+
#
|
|
30
|
+
# @param value [String] - Full path to the cache. If set to nil then caching will be disabled.
|
|
31
|
+
#
|
|
32
|
+
# @see https://proj.org/development/reference/functions.html#c.proj_grid_cache_set_filename
|
|
33
|
+
def path=(value)
|
|
34
|
+
Api.proj_grid_cache_set_filename(self.context, value.encode('UTF-8'))
|
|
35
|
+
value
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Sets the cache size
|
|
39
|
+
#
|
|
40
|
+
# @param value [Integer] Maximum size in Megabytes (1024*1024 bytes), or negative value to set unlimited size.
|
|
41
|
+
#
|
|
42
|
+
# @see https://proj.org/development/reference/functions.html#c.proj_grid_cache_set_max_size
|
|
43
|
+
def max_size=(value)
|
|
44
|
+
Api.proj_grid_cache_set_max_size(self.context, value)
|
|
45
|
+
value
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Specifies the time-to-live delay for re-checking if the cached properties of files are still up-to-date.
|
|
49
|
+
#
|
|
50
|
+
# @param value [Integer] Delay in seconds. Use negative value for no expiration.
|
|
51
|
+
#
|
|
52
|
+
# @see https://proj.org/development/reference/functions.html#c.proj_grid_cache_set_ttl
|
|
53
|
+
def ttl=(value)
|
|
54
|
+
Api.proj_grid_cache_set_ttl(self.context, value)
|
|
55
|
+
value
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Clears the cache
|
|
59
|
+
#
|
|
60
|
+
# @see https://proj.org/development/reference/functions.html#c.proj_grid_cache_clear
|
|
61
|
+
def clear
|
|
62
|
+
Api.proj_grid_cache_clear(self.context)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/lib/proj/grid_info.rb
CHANGED
|
@@ -1,19 +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
|
|
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,21 @@
|
|
|
1
|
+
module Proj
|
|
2
|
+
# Tracks whether a PROJ context is still alive. Shared between a Context and
|
|
3
|
+
# the finalizers of objects that depend on it (PjObject, Session). Because
|
|
4
|
+
# Ruby's GC finalizer ordering is non-deterministic during shutdown, a
|
|
5
|
+
# dependent's finalizer may run after its context has already been destroyed.
|
|
6
|
+
# Calling proj_destroy() in that situation segfaults (PROJ 9.8+). This object
|
|
7
|
+
# lets dependents check before calling into PROJ.
|
|
8
|
+
class LifeSpan
|
|
9
|
+
def initialize
|
|
10
|
+
@alive = true
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def alive?
|
|
14
|
+
@alive
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def ended!
|
|
18
|
+
@alive = false
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
module Proj
|
|
2
|
+
# Include this module in a class to create a custom network API for PROJ.
|
|
3
|
+
# Install it via {Context#set_network_api}.
|
|
4
|
+
#
|
|
5
|
+
# The including class must call {#install_callbacks} in its initializer and
|
|
6
|
+
# implement the following methods:
|
|
7
|
+
#
|
|
8
|
+
# * +open(uri, offset, size_to_read)+ - Open a network connection and perform
|
|
9
|
+
# the initial read. Return a connection object (any Ruby object) that holds
|
|
10
|
+
# the connection state, along with the data read. The return value must be a
|
|
11
|
+
# Hash with at least a +:data+ key containing the bytes read.
|
|
12
|
+
# * +close(connection)+ - Close the network connection.
|
|
13
|
+
# * +header_value(connection, header_name)+ - Return the value of a response header.
|
|
14
|
+
# * +read_range(connection, offset, size_to_read)+ - Read a range of bytes, return a String.
|
|
15
|
+
#
|
|
16
|
+
# The +connection+ parameter passed to close/header_value/read_range is whatever
|
|
17
|
+
# object your +open+ method returned.
|
|
18
|
+
module NetworkApiCallbacks
|
|
19
|
+
def install_callbacks(context)
|
|
20
|
+
@open_cbk = self.method(:open_callback)
|
|
21
|
+
@close_cbk = self.method(:close_callback)
|
|
22
|
+
@header_value_cbk = self.method(:header_value_callback)
|
|
23
|
+
@read_range_cbk = self.method(:read_range_callback)
|
|
24
|
+
|
|
25
|
+
# Maps native address -> {proj_handle:, connection:, header_ptrs: []}
|
|
26
|
+
# Retaining proj_handle prevents the MemoryPointer from being GCed
|
|
27
|
+
# while PROJ holds the address. header_ptrs retains string pointers
|
|
28
|
+
# returned by header_value_callback.
|
|
29
|
+
@network_handles = {}
|
|
30
|
+
|
|
31
|
+
result = Api.proj_context_set_network_callbacks(context, @open_cbk, @close_cbk, @header_value_cbk, @read_range_cbk, nil)
|
|
32
|
+
|
|
33
|
+
if result != 1
|
|
34
|
+
Error.check_object(self)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def open_callback(context, url, offset, size_to_read, buffer, out_size_read, error_string_max_size, out_error_string, user_data)
|
|
39
|
+
uri = URI.parse(url)
|
|
40
|
+
connection = self.open(uri, offset, size_to_read)
|
|
41
|
+
out_size = [size_to_read, connection[:data].size].min
|
|
42
|
+
out_size_read.write(:size_t, out_size)
|
|
43
|
+
buffer.write_bytes(connection[:data], 0, out_size)
|
|
44
|
+
|
|
45
|
+
register_handle(connection)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def close_callback(context, handle, user_data)
|
|
49
|
+
connection = handle_to_connection(handle)
|
|
50
|
+
self.close(connection)
|
|
51
|
+
unregister_handle(handle)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def header_value_callback(context, handle, header_name, user_data)
|
|
55
|
+
connection = handle_to_connection(handle)
|
|
56
|
+
value = self.header_value(connection, header_name)
|
|
57
|
+
ptr = FFI::MemoryPointer.from_string(value)
|
|
58
|
+
@network_handles[handle.address][:header_ptrs] << ptr
|
|
59
|
+
ptr
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def read_range_callback(context, handle, offset, size_to_read, buffer, error_string_max_size, out_error_string, user_data)
|
|
63
|
+
connection = handle_to_connection(handle)
|
|
64
|
+
data = self.read_range(connection, offset, size_to_read)
|
|
65
|
+
out_size = [size_to_read, data.size].min
|
|
66
|
+
buffer.write_bytes(data, 0, out_size)
|
|
67
|
+
out_size
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def register_handle(connection)
|
|
73
|
+
proj_handle = FFI::MemoryPointer.new(:pointer)
|
|
74
|
+
@network_handles[proj_handle.address] = { proj_handle: proj_handle, connection: connection, header_ptrs: [] }
|
|
75
|
+
proj_handle
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def handle_to_connection(handle)
|
|
79
|
+
@network_handles[handle.address][:connection]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def unregister_handle(handle)
|
|
83
|
+
@network_handles.delete(handle.address)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
data/lib/proj/operation.rb
CHANGED
|
@@ -1,43 +1,67 @@
|
|
|
1
|
-
module Proj
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
1
|
+
module Proj
|
|
2
|
+
# Represents a PROJ operation (projection or conversion method).
|
|
3
|
+
# Each operation has an identifier and a human-readable description.
|
|
4
|
+
#
|
|
5
|
+
# @example List all operations
|
|
6
|
+
# operations = Proj::Operation.list
|
|
7
|
+
# operations.each { |op| puts "#{op.id}: #{op.description}" }
|
|
8
|
+
#
|
|
9
|
+
# @example Find a specific operation
|
|
10
|
+
# op = Proj::Operation.get("aea")
|
|
11
|
+
# puts op.description #=> "Albers Equal Area"
|
|
12
|
+
class Operation
|
|
13
|
+
# @!attribute [r] id
|
|
14
|
+
# @return [String] The operation identifier (e.g., "aea", "merc")
|
|
15
|
+
# @!attribute [r] description
|
|
16
|
+
# @return [String] Human-readable description of the operation
|
|
17
|
+
attr_reader :id, :description
|
|
18
|
+
|
|
19
|
+
# Returns a list of all available operations.
|
|
20
|
+
#
|
|
21
|
+
# @return [Array<Operation>]
|
|
22
|
+
def self.list
|
|
23
|
+
pointer_to_array = FFI::Pointer.new(Api::PjList, Api.proj_list_operations)
|
|
24
|
+
result = Array.new
|
|
25
|
+
0.step do |i|
|
|
26
|
+
operation_info = Api::PjList.new(pointer_to_array[i])
|
|
27
|
+
break result if operation_info[:id].nil?
|
|
28
|
+
id = operation_info[:id]
|
|
29
|
+
description = operation_info[:descr].read_pointer.read_string.force_encoding('UTF-8')
|
|
30
|
+
result << self.new(id, description)
|
|
31
|
+
end
|
|
32
|
+
result
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Finds an operation by its identifier.
|
|
36
|
+
#
|
|
37
|
+
# @param id [String] The operation identifier to search for
|
|
38
|
+
#
|
|
39
|
+
# @return [Operation, nil] The matching operation or nil if not found
|
|
40
|
+
def self.get(id)
|
|
41
|
+
self.list.find {|operation| operation.id == id}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Creates a new Operation.
|
|
45
|
+
#
|
|
46
|
+
# @param id [String] The operation identifier
|
|
47
|
+
# @param description [String] Human-readable description
|
|
48
|
+
#
|
|
49
|
+
# @return [Operation]
|
|
50
|
+
def initialize(id, description)
|
|
51
|
+
@id = id
|
|
52
|
+
@description = description
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def ==(other)
|
|
56
|
+
self.id == other.id
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def to_s
|
|
60
|
+
self.id
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def inspect
|
|
64
|
+
"#<#{self.class} id=\"#{id}\", description=\"#{description}\">"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
43
67
|
end
|
|
@@ -21,7 +21,9 @@ module Proj
|
|
|
21
21
|
# If authority is a non-empty string different of "any", then coordinate
|
|
22
22
|
# operations will be searched only in that authority namespace.
|
|
23
23
|
def initialize(context, authority: nil)
|
|
24
|
-
@
|
|
24
|
+
@context = context || Context.current
|
|
25
|
+
Error.validate_context!(@context)
|
|
26
|
+
@pointer = Api.proj_create_operation_factory_context(@context, authority)
|
|
25
27
|
ObjectSpace.define_finalizer(self, self.class.finalize(@pointer))
|
|
26
28
|
end
|
|
27
29
|
|
|
@@ -134,4 +136,4 @@ module Proj
|
|
|
134
136
|
@pointer
|
|
135
137
|
end
|
|
136
138
|
end
|
|
137
|
-
end
|
|
139
|
+
end
|