proj4rb 3.0.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ChangeLog +26 -15
- data/README.rdoc +82 -44
- data/Rakefile +27 -27
- data/lib/api/api.rb +96 -118
- data/lib/api/api_5_0.rb +331 -300
- data/lib/api/api_5_1.rb +6 -6
- data/lib/api/api_5_2.rb +4 -4
- data/lib/api/api_6_0.rb +116 -14
- data/lib/api/api_6_1.rb +4 -4
- data/lib/api/api_6_2.rb +9 -6
- data/lib/api/api_6_3.rb +6 -0
- data/lib/api/api_7_0.rb +68 -0
- data/lib/api/api_7_1.rb +73 -0
- data/lib/api/api_7_2.rb +14 -0
- data/lib/api/api_8_0.rb +6 -0
- data/lib/api/api_8_1.rb +24 -0
- data/lib/api/api_8_2.rb +6 -0
- data/lib/api/api_9_1.rb +7 -0
- data/lib/api/api_9_2.rb +9 -0
- data/lib/api/api_experimental.rb +196 -0
- data/lib/proj/area.rb +73 -32
- data/lib/proj/axis_info.rb +44 -0
- data/lib/proj/bounds.rb +13 -0
- data/lib/proj/context.rb +174 -28
- data/lib/proj/conversion.rb +92 -0
- data/lib/proj/coordinate.rb +281 -197
- data/lib/proj/coordinate_operation_mixin.rb +381 -0
- data/lib/proj/coordinate_system.rb +137 -0
- data/lib/proj/crs.rb +672 -204
- data/lib/proj/crs_info.rb +47 -0
- data/lib/proj/database.rb +305 -0
- data/lib/proj/datum.rb +32 -0
- data/lib/proj/datum_ensemble.rb +34 -0
- data/lib/proj/ellipsoid.rb +77 -41
- data/lib/proj/error.rb +62 -9
- data/lib/proj/file_api.rb +166 -0
- data/lib/proj/grid.rb +121 -0
- data/lib/proj/grid_cache.rb +64 -0
- data/lib/proj/grid_info.rb +19 -0
- data/lib/proj/network_api.rb +92 -0
- data/lib/proj/operation.rb +42 -42
- data/lib/proj/operation_factory_context.rb +136 -0
- data/lib/proj/parameter.rb +38 -0
- data/lib/proj/parameters.rb +106 -0
- data/lib/proj/pj_object.rb +670 -80
- data/lib/proj/pj_objects.rb +44 -0
- data/lib/proj/prime_meridian.rb +65 -39
- data/lib/proj/projection.rb +698 -207
- data/lib/proj/session.rb +46 -0
- data/lib/proj/strings.rb +32 -0
- data/lib/proj/transformation.rb +101 -60
- data/lib/proj/unit.rb +108 -53
- data/lib/proj.rb +110 -9
- data/proj4rb.gemspec +5 -5
- data/test/abstract_test.rb +23 -1
- data/test/context_test.rb +172 -82
- data/test/conversion_test.rb +368 -0
- data/test/coordinate_system_test.rb +144 -0
- data/test/crs_test.rb +770 -71
- data/test/database_test.rb +360 -0
- data/test/datum_ensemble_test.rb +65 -0
- data/test/datum_test.rb +55 -0
- data/test/ellipsoid_test.rb +64 -18
- data/test/file_api_test.rb +66 -0
- data/test/grid_cache_test.rb +72 -0
- data/test/grid_test.rb +141 -0
- data/test/network_api_test.rb +45 -0
- data/test/operation_factory_context_test.rb +201 -0
- data/test/parameters_test.rb +40 -0
- data/test/pj_object_test.rb +179 -0
- data/test/prime_meridian_test.rb +76 -0
- data/test/proj_test.rb +46 -4
- data/test/projection_test.rb +646 -222
- data/test/session_test.rb +78 -0
- data/test/transformation_test.rb +149 -7
- data/test/unit_test.rb +57 -28
- metadata +51 -13
- data/lib/api/api_4_9.rb +0 -31
- data/lib/proj/config.rb +0 -70
- data/lib/proj/point.rb +0 -72
- 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
|
data/lib/proj/operation.rb
CHANGED
@@ -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
|