proj4rb 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +5 -5
  2. data/ChangeLog +46 -0
  3. data/Gemfile +4 -0
  4. data/README.rdoc +158 -148
  5. data/Rakefile +26 -41
  6. data/lib/area.rb +32 -0
  7. data/lib/config.rb +70 -0
  8. data/lib/context.rb +103 -0
  9. data/lib/coordinate.rb +197 -0
  10. data/lib/crs.rb +206 -0
  11. data/lib/ellipsoid.rb +42 -0
  12. data/lib/error.rb +18 -0
  13. data/lib/operation.rb +43 -0
  14. data/lib/pj_object.rb +82 -0
  15. data/lib/point.rb +72 -0
  16. data/lib/prime_meridian.rb +40 -0
  17. data/lib/proj.rb +31 -0
  18. data/lib/proj4.rb +3 -469
  19. data/lib/projection.rb +207 -0
  20. data/lib/transformation.rb +61 -0
  21. data/lib/unit.rb +54 -0
  22. data/proj4rb.gemspec +31 -0
  23. data/test/abstract_test.rb +7 -0
  24. data/test/context_test.rb +82 -0
  25. data/test/coordinate_test.rb +35 -0
  26. data/test/crs_test.rb +373 -0
  27. data/test/ellipsoid_test.rb +34 -0
  28. data/test/operation_test.rb +29 -0
  29. data/test/prime_meridians_test.rb +33 -0
  30. data/test/proj_test.rb +17 -0
  31. data/test/projection_test.rb +224 -0
  32. data/test/transformation_test.rb +68 -0
  33. data/test/unit_test.rb +47 -0
  34. metadata +82 -77
  35. data/data/GL27 +0 -22
  36. data/data/MD +0 -0
  37. data/data/TN +0 -0
  38. data/data/WI +0 -0
  39. data/data/WO +0 -0
  40. data/data/conus +0 -0
  41. data/data/epsg +0 -5443
  42. data/data/epsg-deprecated +0 -2
  43. data/data/esri +0 -5937
  44. data/data/esri.extra +0 -948
  45. data/data/hawaii +0 -0
  46. data/data/nad.lst +0 -142
  47. data/data/nad27 +0 -809
  48. data/data/nad83 +0 -744
  49. data/data/ntv1_can.dat +0 -0
  50. data/data/null +0 -0
  51. data/data/other.extra +0 -49
  52. data/data/proj_def.dat +0 -17
  53. data/data/prvi +0 -0
  54. data/data/stgeorge +0 -0
  55. data/data/stlrnc +0 -0
  56. data/data/stpaul +0 -0
  57. data/data/world +0 -212
  58. data/example/basic.rb +0 -18
  59. data/example/list-datums.rb +0 -17
  60. data/example/list-ellipsoids.rb +0 -17
  61. data/example/list-errors.rb +0 -11
  62. data/example/list-prime-meridians.rb +0 -17
  63. data/example/list-projection-types.rb +0 -17
  64. data/example/list-units.rb +0 -17
  65. data/example/version.rb +0 -8
  66. data/ext/Makefile +0 -238
  67. data/ext/extconf.rb +0 -16
  68. data/ext/mkmf.log +0 -103
  69. data/ext/out.log +0 -0
  70. data/ext/proj4_ruby-x64-mingw32.def +0 -2
  71. data/ext/proj4_ruby.so +0 -0
  72. data/ext/projrb.c +0 -566
  73. data/ext/projrb.o +0 -0
  74. data/ext/vc/proj4_ruby.sln +0 -19
  75. data/ext/vc/proj4_ruby.vcproj +0 -208
  76. data/test/test_constants.rb +0 -18
  77. data/test/test_create_projection.rb +0 -63
  78. data/test/test_datums.rb +0 -45
  79. data/test/test_ellipsoids.rb +0 -46
  80. data/test/test_errors.rb +0 -66
  81. data/test/test_init_projection.rb +0 -109
  82. data/test/test_prime_meridians.rb +0 -45
  83. data/test/test_projection_type.rb +0 -44
  84. data/test/test_simple_projection.rb +0 -58
  85. data/test/test_suite.rb +0 -14
  86. data/test/test_transform.rb +0 -115
  87. data/test/test_units.rb +0 -46
@@ -0,0 +1,207 @@
1
+ # encoding: UTF-8
2
+
3
+ module Proj
4
+ # @deprecated This class is *DEPRECATED.* It will be removed when Proj 7 is released and removes the
5
+ # underlying API's this class uses. Code should be ported to use Crs and Transformation objects.
6
+ class Projection
7
+ def self.parse(value)
8
+ values = case value
9
+ when Array
10
+ value
11
+ when String
12
+ value.strip.split(' ')
13
+ when Hash
14
+ array = []
15
+ value.each_pair do |key, value|
16
+ key = "+#{key}"
17
+ array << (value.nil? ? key : "#{key}=#{value}")
18
+ end
19
+ array
20
+ when Projection
21
+ value.getDef.split(' ')
22
+ else
23
+ raise ArgumentError, "Unknown type #{value.class} for projection definition"
24
+ end
25
+ end
26
+
27
+ def self.finalize(pointer)
28
+ proc do
29
+ Api.pj_free(pointer)
30
+ end
31
+ end
32
+
33
+ # Projection classes are created using Proj4 strings which consist of a number of parameters.
34
+ # For more information please see the +opt arguments section at https://proj.org/apps/proj.html.
35
+ #
36
+ # @param value [string, array, hash] Parameters can be specified as strings, arrays or hashes.
37
+ #
38
+ # @example
39
+ # proj = Projection.new("+proj=utm +zone=21 +units=m")
40
+ # proj = Projection.new ["+proj=utm", "+zone=21", "+units=m"]
41
+ # proj = Projection.new("proj" => "utm", "zone" => "21", "units" => "m")
42
+ #
43
+ # With all variants the plus sign in front of the keys is optional.
44
+ def initialize(value)
45
+ params = self.class.parse(value)
46
+ p_params = FFI::MemoryPointer.new(:pointer, params.length)
47
+ params.each_with_index do |param, i|
48
+ p_param = FFI::MemoryPointer.from_string(param)
49
+ p_params[i].write_pointer(p_param)
50
+ end
51
+
52
+ @pointer = Api.pj_init(params.count, p_params)
53
+ self.check_error
54
+
55
+ ObjectSpace.define_finalizer(self, self.class.finalize(@pointer))
56
+ end
57
+
58
+ def check_error
59
+ ptr = Api.pj_get_errno_ref
60
+ errno = ptr.read_int
61
+ if errno != 0
62
+ # If we don't reset the error code it hangs around. This doesn't seem documented anyplace?
63
+ ptr.write_int(0)
64
+ Error.check(errno)
65
+ end
66
+ end
67
+
68
+
69
+ def to_ptr
70
+ @pointer
71
+ end
72
+
73
+ # Returns projection definitions
74
+ #
75
+ # @return [String]
76
+ def getDef
77
+ Api.pj_get_def(self, 0)
78
+ end
79
+
80
+ # Returns if this is a geocentric projection
81
+ #
82
+ # @return [Boolean]
83
+ def isGeocent?
84
+ Api::pj_is_geocent(self)
85
+ end
86
+ alias :isGeocentric? :isGeocent?
87
+
88
+ # Returns if this is a lat/long projection
89
+ #
90
+ # @return [Boolean]
91
+ def isLatLong?
92
+ Api::pj_is_latlong(self)
93
+ end
94
+
95
+ # Get the ID of this projection.
96
+ #
97
+ # @return [String]
98
+ def projection
99
+ getDef =~ /\+proj=(.+?) / ? $1 : nil
100
+ end
101
+
102
+ # Get the ID of the datum used in this projection.
103
+ #
104
+ # @return [String]
105
+ def datum
106
+ getDef =~ /\+datum=(.+?) / ? $1 : nil
107
+ end
108
+
109
+ # Get definition of projection in typical inspect format (#<Proj::Projection +init=... +proj=... ...>).
110
+ #
111
+ # @return [String]
112
+ def to_s
113
+ "#<#{self.class.name}#{getDef}>"
114
+ end
115
+
116
+ # Forward projection of a point. Returns a copy of the point object with coordinates projected.
117
+ #
118
+ # @param point [Point] in radians
119
+ # @return [Point] in cartesian coordinates
120
+ def forward(point)
121
+ struct = Api.pj_fwd(point, self)
122
+ self.check_error
123
+ Point.from_pointer(struct)
124
+ end
125
+
126
+ # Convenience function for calculating a forward projection with degrees instead of radians.
127
+ #
128
+ # @param point [Point] in degrees
129
+ # @return [Point] in cartesian coordinates
130
+ def forwardDeg(point)
131
+ forward(point.to_radians)
132
+ end
133
+
134
+ # Projects all points in a collection.
135
+ #
136
+ # @param collection [Enumerable<Point>] Points specified in radians
137
+ # @return [Enumerable<Point>] Points specified in cartesian coordinates
138
+ def forward_all(collection)
139
+ collection.map do |point|
140
+ forward(point)
141
+ end
142
+ end
143
+
144
+ # Inverse projection of a point. Returns a copy of the point object with coordinates projected.
145
+ #
146
+ # @param point [Point] in cartesian coordinates
147
+ # @return [Point] in radians
148
+ def inverse(point)
149
+ struct = Api.pj_inv(point, self)
150
+ self.check_error
151
+ Point.from_pointer(struct)
152
+ end
153
+
154
+ # Convenience function for calculating an inverse projection with the result in degrees instead of radians.
155
+ #
156
+ # @param point [Point] in cartesian coordinates
157
+ # @return [Point] in degrees
158
+ def inverseDeg(point)
159
+ result = inverse(point)
160
+ result.to_degrees
161
+ end
162
+
163
+ # Inverse projection of all points in a collection.
164
+ #
165
+ # @param collection [Enumerable<Point>] Points specified in cartesian coordinates
166
+ # @return [Enumerable<Point>] Points specified in radians
167
+ def inverse_all(collection)
168
+ collection.map do |point|
169
+ inverse(point)
170
+ end
171
+ end
172
+
173
+ # Transforms a point from one projection to another.
174
+ #
175
+ # @param other [Projection]
176
+ # @param point [Point]
177
+ # @return [Point]
178
+ def transform(other, point)
179
+ p_x = FFI::MemoryPointer.new(:double, 1)
180
+ p_x.write_double(point.x)
181
+
182
+ p_y = FFI::MemoryPointer.new(:double, 1)
183
+ p_y.write_double(point.y)
184
+
185
+ p_z = FFI::MemoryPointer.new(:double, 1)
186
+ p_z.write_double(0)
187
+
188
+ a = Api.pj_transform(self, other, 1, 1, p_x, p_y, p_z)
189
+ self.check_error
190
+
191
+ Point.new(p_x.read_double, p_y.read_double)
192
+ end
193
+
194
+ # Transforms all points in a collection from one projection to
195
+ # another. The +collection+ object must implement the +each+,
196
+ # +clear+, and << methods (just like an Array) for this to work.
197
+ #
198
+ # @param other [Projection]
199
+ # @param collection [Enumerable, Point]
200
+ # @return [Enumerable, Point]
201
+ def transform_all(other, collection)
202
+ collection.map do |point|
203
+ transform(other, point)
204
+ end
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,61 @@
1
+ module Proj
2
+ # Transformation objects convert {Coordinate Coordinates} from one {Crs} to another.
3
+ class Transformation < PjObject
4
+ # Transforms a {Coordinate} from the source {Crs} to the target {Crs}. Coordinates should be expressed in
5
+ # the units and axis order of the definition of the source CRS. The returned transformed coordinate will
6
+ # be in the units and axis order of the definition of the target CRS.
7
+ #
8
+ # For most geographic Crses, the units will be in degrees. For geographic CRS defined by the EPSG authority,
9
+ # the order of coordinates is latitude first, longitude second. When using a PROJ initialization string,
10
+ # on contrary, the order will be longitude first, latitude second.
11
+ #
12
+ # For projected CRS, the units may vary (metre, us-foot, etc..).
13
+ #
14
+ # For projected CRS defined by the EPSG authority, and with EAST / NORTH directions, the axis order might be
15
+ # easting first, northing second, or the reverse. When using a PROJ string, the order will be
16
+ # easting first, northing second, except if the +axis parameter modifies it.
17
+ #
18
+ # @param source [Crs | String] - The source Crs. See the Crs documentation for the string format
19
+ # @param target [Crs | String] - The target Crs. See the Crs documentation for the string format
20
+ # @param context [Context]
21
+ def initialize(source, target, context=nil)
22
+ pointer = if source.is_a?(Crs) && target.is_a?(Crs)
23
+ if Api.method_defined?(:proj_create_crs_to_crs_from_pj)
24
+ Api.proj_create_crs_to_crs_from_pj(self.context, source, target, nil, nil)
25
+ else
26
+ Api.proj_create_crs_to_crs(self.context, source.definition, target.definition, nil)
27
+ end
28
+ else
29
+ Api.proj_create_crs_to_crs(self.context, source, target, nil)
30
+ end
31
+
32
+ if pointer.null?
33
+ Error.check
34
+ end
35
+
36
+ super(pointer, context)
37
+ end
38
+
39
+ # Transforms a {Coordinate} from the source {Crs} to the target {Crs}. Coordinates should be expressed in
40
+ # the units and axis order of the definition of the source CRS. The returned transformed coordinate will
41
+ # be in the units and axis order of the definition of the target CRS.
42
+ #
43
+ # @param coord [Coordinate]
44
+ # @return [Coordinate]
45
+ def forward(coord)
46
+ struct = Api.proj_trans(self, :PJ_FWD, coord)
47
+ Coordinate.from_coord(struct)
48
+ end
49
+
50
+ # Transforms a {Coordinate} from the target {Crs} to the source {Crs}. Coordinates should be expressed in
51
+ # the units and axis order of the definition of the source CRS. The returned transformed coordinate will
52
+ # be in the units and axis order of the definition of the target CRS.
53
+ #
54
+ # @param coord [Coordinate]
55
+ # @return [Coordinate]
56
+ def inverse(coord)
57
+ struct = Api.proj_trans(self, :PJ_INV, coord)
58
+ Coordinate.from_coord(struct)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,54 @@
1
+ module Proj
2
+ class Unit
3
+ attr_reader :id, :to_meter, :factor, :name
4
+
5
+ def self.list
6
+ # First get linear units
7
+ pointer_to_array = FFI::Pointer.new(Api::PJ_UNITS, Api.proj_list_units)
8
+ result = Array.new
9
+ 0.step do |i|
10
+ ellipse_info = Api::PJ_UNITS.new(pointer_to_array[i])
11
+ break if ellipse_info[:id].nil?
12
+ result << self.new(ellipse_info[:id], ellipse_info[:to_meter], ellipse_info[:factor], ellipse_info[:name])
13
+ end
14
+
15
+ # Now get angular linear units
16
+ if Api.method_defined?(:proj_list_angular_units)
17
+ pointer_to_array = FFI::Pointer.new(Api::PJ_UNITS, Api.proj_list_angular_units)
18
+ 0.step do |i|
19
+ ellipse_info = Api::PJ_UNITS.new(pointer_to_array[i])
20
+ break result if ellipse_info[:id].nil?
21
+ result << self.new(ellipse_info[:id], ellipse_info[:to_meter], ellipse_info[:factor], ellipse_info[:name])
22
+ end
23
+ end
24
+ result
25
+ end
26
+
27
+ def self.get(id)
28
+ self.list.find {|ellipsoid| ellipsoid.id == id}
29
+ end
30
+
31
+ def initialize(id, to_meter, factor, name)
32
+ @id = id
33
+ @to_meter = to_meter
34
+ @factor = factor
35
+ @name = name
36
+ end
37
+
38
+ def <=>(other)
39
+ self.id <=> other.id
40
+ end
41
+
42
+ def ==(other)
43
+ self.id == other.id
44
+ end
45
+
46
+ def to_s
47
+ self.id
48
+ end
49
+
50
+ def inspect
51
+ "#<#{self.class} id=\"#{id}\", to_meter=\"#{to_meter}\", factor=\"#{factor}\", name=\"#{name}\">"
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,31 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = 'proj4rb'
3
+ spec.version = '2.0.0'
4
+ spec.summary = 'Ruby bindings for the Proj.4 Carthographic Projection library'
5
+ spec.description = <<-EOF
6
+ Proj4rb is a ruby binding for the Proj.4 Carthographic Projection library, that supports conversions between a very large number of geographic coordinate systems and datumspec.
7
+ EOF
8
+ spec.platform = Gem::Platform::RUBY
9
+ spec.authors = ['Guilhem Vellut', 'Jochen Topf', 'Charlie Savage']
10
+ spec.homepage = 'https://github.com/cfis/proj4rb'
11
+ spec.required_ruby_version = '>= 2.4.9'
12
+ spec.license = 'MIT'
13
+
14
+ spec.requirements << 'Proj (Proj4) Library'
15
+ spec.require_path = 'lib'
16
+ spec.files = Dir['ChangeLog',
17
+ 'Gemfile',
18
+ 'MIT-LICENSE',
19
+ 'proj4rb.gemspec',
20
+ 'Rakefile',
21
+ 'README.rdoc',
22
+ 'lib/*.rb',
23
+ 'test/*.rb']
24
+
25
+ spec.test_files = Dir["test/test_*.rb"]
26
+
27
+ spec.add_dependency "ffi"
28
+
29
+ spec.add_development_dependency('minitest')
30
+ spec.add_development_dependency('yard')
31
+ end
@@ -0,0 +1,7 @@
1
+ require 'bundler/setup'
2
+ require 'minitest/autorun'
3
+ require 'proj'
4
+
5
+ class AbstractTest < Minitest::Test
6
+ end
7
+
@@ -0,0 +1,82 @@
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