ffi-proj4 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,92 @@
1
+
2
+ module Proj4
3
+ class Error < StandardError
4
+ ERRORS = %w{
5
+ Unknown
6
+ NoArgsInInitList
7
+ NoOptionsInInitFile
8
+ NoColonInInitString
9
+ ProjectionNotNamed
10
+ UnknownProjectionId
11
+ EffectiveEccentricityEq1
12
+ UnknownUnitConversionId
13
+ InvalidBooleanParamArgument
14
+ UnknownEllipticalParameterName
15
+ ReciprocalFlatteningIsZero
16
+ RadiusReferenceLatitudeGt90
17
+ SquaredEccentricityLessThanZero
18
+ MajorAxisOrRadiusIsZeroOrNotGiven
19
+ LatitudeOrLongitudeExceededLimits
20
+ InvalidXOrY
21
+ ImproperlyFormedDMSValue
22
+ NonConvergentInverseMeridinalDist
23
+ NonConvergentInversePhi2
24
+ AcosOrAsinArgTooBig
25
+ ToleranceCondition
26
+ ConicLat1EqMinusLat2
27
+ Lat1GreaterThan90
28
+ Lat1IsZero
29
+ LatTsGreater90
30
+ NoDistanceBetweenControlPoints
31
+ ProjectionNotSelectedToBeRotated
32
+ WSmallerZeroOrMSmallerZero
33
+ LsatNotInRange
34
+ PathNotInRange
35
+ HSmallerZero
36
+ KSmallerZero
37
+ Lat0IsZeroOr90OrAlphaIsZero
38
+ Lat1EqLat2OrLat1IsZeroOrLat2Is90
39
+ EllipticalUsageRequired
40
+ InvalidUTMZoneNumber
41
+ ArgsOutOfRangeForTchebyEval
42
+ NoProjectionToBeRotated
43
+ FailedToLoadNAD2783CorrectionFile
44
+ BothNAndMMustBeSpecdAndGreaterZero
45
+ NSmallerZeroOrNGreaterOneOrNotSpecified
46
+ Lat1OrLat2NotSpecified
47
+ AbsoluteLat1EqLat2
48
+ Lat0IsHalfPiFromMeanLat
49
+ UnparseableCoordinateSystemDefinition
50
+ GeocentricTransformationMissingZOrEllps
51
+ UnknownPrimeMeridianConversionId
52
+ IllegalAxisOrientationCombination
53
+ PointNotWithinAvailableDatumShiftGrids
54
+ InvalidSweepAxis
55
+ }
56
+
57
+ def self.list
58
+ ERRORS
59
+ end
60
+
61
+ def self.error(errno)
62
+ ERRORS[errno.abs] || 'Unknown'
63
+ end
64
+
65
+ def self.raise_error(errno)
66
+ raise self.instantiate_error(errno), caller[0..-1]
67
+ end
68
+
69
+ def self.instantiate_error(errno)
70
+ name = self.error(errno)
71
+ Proj4.const_get("#{name}Error").new(Proj4::FFIProj4.pj_strerrno(errno))
72
+ end
73
+
74
+ def errno
75
+ self.class.errno
76
+ end
77
+ alias :errnum :errno
78
+ end
79
+
80
+ Error::ERRORS.each_with_index do |err, index|
81
+ Proj4.module_eval(<<-EOF, __FILE__, __LINE__ + 1)
82
+ class #{err}Error < Proj4::Error
83
+ class << self
84
+ def errno
85
+ #{index}
86
+ end
87
+ alias :errnum :errno
88
+ end
89
+ end
90
+ EOF
91
+ end
92
+ end
@@ -0,0 +1,39 @@
1
+
2
+ module Proj4
3
+ class Point
4
+ include Proj4::Tools
5
+
6
+ attr_accessor :x, :y, :z
7
+
8
+ def initialize(x, y, z = nil)
9
+ @x, @y, @z = x, y, z
10
+ end
11
+
12
+ alias :lon :x
13
+ alias :lon= :x=
14
+ alias :lat :y
15
+ alias :lat= :y=
16
+
17
+ def to_deg!
18
+ self.x = rad_to_deg(self.x)
19
+ self.y = rad_to_deg(self.y)
20
+ self.z = rad_to_deg(self.z) unless self.z.nil?
21
+ self
22
+ end
23
+
24
+ def to_deg
25
+ self.dup.to_deg!
26
+ end
27
+
28
+ def to_rad!
29
+ self.x = deg_to_rad(self.x)
30
+ self.y = deg_to_rad(self.y)
31
+ self.z = deg_to_rad(self.z) unless self.z.nil?
32
+ self
33
+ end
34
+
35
+ def to_rad
36
+ self.dup.to_rad!
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,310 @@
1
+
2
+ module Proj4
3
+ class ProjectionParseError < RuntimeError; end
4
+
5
+ class Projection
6
+ include Tools
7
+
8
+ attr_reader :ptr
9
+
10
+ def initialize(arg, auto_free = true)
11
+ args = case arg
12
+ when Array
13
+ arg.collect { |a| a.sub(/^\+/, '') }
14
+ when String
15
+ if arg =~ /^(epsg|esri):/i
16
+ [ "+init=#{arg}" ]
17
+ else
18
+ arg.strip.split(/ /).collect { |a| a.sub(/^\+/, '') }
19
+ end
20
+ when Hash
21
+ arg.collect { |k, v|
22
+ if v.nil?
23
+ k.to_s
24
+ else
25
+ "#{k.to_s.strip}=#{v.to_s.strip}"
26
+ end
27
+ }
28
+ when Proj4::Projection
29
+ arg.definition.strip.split(/ /).collect { |a| a.sub(/^\+/, '') }
30
+ else
31
+ raise ArgumentError.new("Unknown type #{arg.class} for projection definition")
32
+ end
33
+
34
+ params = args.collect(&:strip).collect { |a|
35
+ if !(a =~ /^\+/)
36
+ "+#{a}"
37
+ else
38
+ a
39
+ end
40
+ }.join(' ')
41
+
42
+ ptr = FFIProj4.pj_init_plus(params)
43
+
44
+ if ptr.null?
45
+ result = FFIProj4.pj_get_errno_ref.read_int
46
+ raise ProjectionParseError.new(FFIProj4.pj_strerrno(result))
47
+ else
48
+ @ptr = FFI::AutoPointer.new(
49
+ ptr,
50
+ self.class.method(:release)
51
+ )
52
+ end
53
+
54
+ self.ptr.autorelease = auto_free
55
+ end
56
+
57
+ def self.release(ptr) #:nodoc:
58
+ FFIProj4.pj_free(ptr)
59
+ end
60
+
61
+ def lat_long?
62
+ bool_result(FFIProj4.pj_is_latlong(self.ptr))
63
+ end
64
+ alias :isLatLong? :lat_long?
65
+
66
+ def geocentric?
67
+ bool_result(FFIProj4.pj_is_geocent(self.ptr))
68
+ end
69
+ alias :isGeocent? :geocentric?
70
+ alias :isGeocentric? :geocentric?
71
+
72
+ def definition
73
+ @definition ||= FFIProj4.pj_get_def(self.ptr, 0).strip
74
+ end
75
+ alias :getDef :definition
76
+
77
+ def to_hash
78
+ @hash ||= self.definition.split(/ /).inject({}) { |memo, opt|
79
+ memo.tap {
80
+ k, v = opt.split('=')
81
+ k.sub!(/^\+/, '')
82
+ v = true if v.nil?
83
+ memo[k.to_sym] = v
84
+ }
85
+ }
86
+ end
87
+ alias :definition_as_hash :to_hash
88
+
89
+ def to_s
90
+ "#<Proj4::Projection #{definition}>"
91
+ end
92
+ alias :inspect :to_s
93
+
94
+ def forward!(*args)
95
+ xy, point = xy_and_point_from_args(*args)
96
+
97
+ ret = FFIProj4.pj_fwd(xy, self.ptr)
98
+ result = FFIProj4.pj_get_errno_ref.read_int
99
+
100
+ if result == 0
101
+ point.x = ret[:x]
102
+ point.y = ret[:y]
103
+ point.z = 0 if point.respond_to?(:z=)
104
+ point
105
+ else
106
+ raise Proj4::Error.instantiate_error(result)
107
+ end
108
+ end
109
+ alias :forward_rad! :forward!
110
+ alias :forwardRad! :forward!
111
+
112
+ def forward(*args)
113
+ self.forward!(*dup_args(*args))
114
+ end
115
+ alias :forward_rad :forward
116
+ alias :forwardRad :forward
117
+
118
+ def forward_deg!(*args)
119
+ self.forward!(*args_deg_to_rad(*args))
120
+ end
121
+ alias :forwardDeg! :forward_deg!
122
+
123
+ def forward_deg(*args)
124
+ self.forward_deg!(*dup_args(*args))
125
+ end
126
+ alias :forwardDeg :forward_deg
127
+
128
+ def forward_all!(collection)
129
+ collection.each do |args|
130
+ self.forward_all!(proj, *args)
131
+ end
132
+ collection
133
+ end
134
+
135
+ def forward_all(proj, collection)
136
+ collection.collect do |args|
137
+ self.forward!(proj, *(dup_args(*args)))
138
+ end
139
+ end
140
+
141
+ def inverse!(*args)
142
+ xy, point = xy_and_point_from_args(*args)
143
+
144
+ ret = FFIProj4.pj_inv(xy, self.ptr)
145
+ result = FFIProj4.pj_get_errno_ref.read_int
146
+
147
+ if result == 0
148
+ point.x = ret[:x]
149
+ point.y = ret[:y]
150
+ point.z = 0 if point.respond_to?(:z=)
151
+ point
152
+ else
153
+ raise Proj4::Error.instantiate_error(result)
154
+ end
155
+ end
156
+ alias :inverse_rad! :inverse!
157
+ alias :inverseRad! :inverse!
158
+
159
+ def inverse(*args)
160
+ self.inverse!(*dup_args(*args))
161
+ end
162
+ alias :inverse_rad :inverse
163
+ alias :inverseRad :inverse
164
+
165
+ def inverse_deg!(*args)
166
+ self.inverse!(*args).to_deg!
167
+ end
168
+ alias :inverseDeg! :inverse_deg!
169
+
170
+ def inverse_deg(*args)
171
+ self.inverse_deg!(*dup_args(*args))
172
+ end
173
+ alias :inverseDeg :inverse_deg
174
+
175
+ def transform!(proj, *args)
176
+ perform_transform(:pj_transform, proj, *args)
177
+ end
178
+ alias :transform_rad! :transform!
179
+ alias :transformRad! :transform!
180
+
181
+ def transform(proj, *args)
182
+ self.transform!(proj, *(dup_args(*args)))
183
+ end
184
+ alias :transform_rad :transform
185
+ alias :transformRad :transform
186
+
187
+ def transform_deg!(proj, *args)
188
+ self.transform!(proj, *args).to_deg!
189
+ end
190
+
191
+ def transform_deg(proj, *args)
192
+ self.transform_deg!(proj, *(dup_args(*args)))
193
+ end
194
+
195
+ def datum_transform!(proj, *args)
196
+ perform_transform(:pj_datum_transform, proj, *args)
197
+ end
198
+ alias :datum_transform_rad! :datum_transform!
199
+
200
+ def datum_transform(proj, *args)
201
+ self.datum_transform!(proj, *(dup_args(*args)))
202
+ end
203
+
204
+ def datum_transform_deg!(proj, *args)
205
+ self.datum_transform!(proj, *args).to_deg!
206
+ end
207
+
208
+ def datum_transform_deg(proj, *args)
209
+ self.datum_transform_deg!(proj, *(dup_args(*args)))
210
+ end
211
+
212
+ def transform_all!(proj, collection)
213
+ collection.each do |args|
214
+ self.transform!(proj, *args)
215
+ end
216
+ collection
217
+ end
218
+
219
+ def transform_all(proj, collection)
220
+ collection.collect do |args|
221
+ self.transform!(proj, *(dup_args(*args)))
222
+ end
223
+ end
224
+
225
+ def projection
226
+ self.to_hash[:proj]
227
+ end
228
+
229
+ def datum
230
+ self.to_hash[:datum]
231
+ end
232
+
233
+ private
234
+ def xy_and_point_from_args(*args)
235
+ if args.length == 1
236
+ point = args.first
237
+ if point.is_a?(Proj4::ProjXY)
238
+ [ point, Proj4::Point.new(point.x, point.y) ]
239
+ elsif point.respond_to?(:x) && point.respond_to?(:y)
240
+ [ Proj4::ProjXY.alloc_in.init(point.x, point.y), point ]
241
+ else
242
+ raise ArgumentError.new("Expected a Proj4::ProjXY, a Proj4::Point or an object that responds to x and y methods.")
243
+ end
244
+ elsif args.length == 2
245
+ [ Proj4::ProjXY.alloc_in.init(args[0], args[1]), Proj4::Point.new(args[0], args[1]) ]
246
+ else
247
+ raise ArgumentError.new("Wrong number of arguments #{args.length} for 1-2")
248
+ end
249
+ end
250
+
251
+ def point_from_args(*args)
252
+ if args.length >= 2
253
+ Proj4::Point.new(*args)
254
+ elsif args.length == 1 && args.first.respond_to?(:x) && args.first.respond_to?(:y)
255
+ args.first
256
+ else
257
+ raise ArgumentError.new("Expected either coordinates, a Proj4::Point or an object that responds to x and y methods.")
258
+ end
259
+ end
260
+
261
+ def args_deg_to_rad(*args)
262
+ args.collect { |value|
263
+ deg_to_rad!(value)
264
+ }
265
+ end
266
+
267
+ def args_rad_to_deg(*args)
268
+ args.collect { |value|
269
+ rad_to_deg!(value)
270
+ }
271
+ end
272
+
273
+ def dup_args(*args)
274
+ args.collect { |value|
275
+ if !value.is_a?(Numeric) && value.respond_to?(:dup)
276
+ value.dup
277
+ else
278
+ value
279
+ end
280
+ }
281
+ end
282
+
283
+ def perform_transform(transform_method, proj, *args)
284
+ if !proj.is_a?(Proj4::Projection)
285
+ raise TypeError.new("Expected a Proj4::Projection")
286
+ end
287
+
288
+ point = point_from_args(*args)
289
+
290
+ x_ptr = FFI::MemoryPointer.new(:double)
291
+ y_ptr = FFI::MemoryPointer.new(:double)
292
+ z_ptr = FFI::MemoryPointer.new(:double)
293
+
294
+ x_ptr.write_double(point.x)
295
+ y_ptr.write_double(point.y)
296
+ z_ptr.write_double(point.z.nil? ? 0 : point.z) if point.respond_to?(:z)
297
+
298
+ result = FFIProj4.send(transform_method, self.ptr, proj.ptr, 1, 1, x_ptr, y_ptr, z_ptr)
299
+
300
+ if result >= 0 && !bool_result(result)
301
+ point.x = x_ptr.read_double
302
+ point.y = y_ptr.read_double
303
+ point.z = z_ptr.read_double if point.respond_to?(:z=)
304
+ point
305
+ else
306
+ raise Proj4::Error.instantiate_error(result)
307
+ end
308
+ end
309
+ end
310
+ end
@@ -0,0 +1,64 @@
1
+
2
+ module Proj4
3
+ class ProjXY < FFI::Struct
4
+ layout(
5
+ :x, :double,
6
+ :y, :double
7
+ )
8
+
9
+ def initialize(*args)
10
+ case args.first
11
+ when FFI::Pointer, FFI::Buffer
12
+ super
13
+ else
14
+ super()
15
+ self.init(*args)
16
+ end
17
+ end
18
+
19
+ def init(*args)
20
+ if !args.empty?
21
+ self[:x] = args[0].to_f
22
+ self[:y] = args[1].to_f
23
+ end
24
+
25
+ self
26
+ end
27
+
28
+ def x=(v)
29
+ self[:x] = v
30
+ end
31
+
32
+ def y=(v)
33
+ self[:y] = v
34
+ end
35
+
36
+ def x
37
+ self[:x]
38
+ end
39
+
40
+ def y
41
+ self[:y]
42
+ end
43
+
44
+ def to_deg!
45
+ self[:x] = self[:x] * Proj4::RAD_TO_DEG
46
+ self[:y] = self[:y] * Proj4::RAD_TO_DEG
47
+ self
48
+ end
49
+
50
+ def to_deg
51
+ self.dup.to_deg!
52
+ end
53
+
54
+ def to_rad!
55
+ self[:x] = self[:x] * Proj4::DEG_TO_RAD
56
+ self[:y] = self[:y] * Proj4::DEG_TO_RAD
57
+ self
58
+ end
59
+
60
+ def to_rad
61
+ self.dup.to_rad!
62
+ end
63
+ end
64
+ end