ffi-proj4 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/Gemfile +18 -0
- data/Guardfile +17 -0
- data/MIT-LICENSE +22 -0
- data/README.rdoc +29 -0
- data/Rakefile +27 -0
- data/data/FL +0 -0
- data/data/GL27 +22 -0
- data/data/IGNF +487 -0
- data/data/MD +0 -0
- data/data/TN +0 -0
- data/data/WI +0 -0
- data/data/WO +0 -0
- data/data/alaska +0 -0
- data/data/conus +0 -0
- data/data/epsg +8464 -0
- data/data/esri +5937 -0
- data/data/esri.extra +948 -0
- data/data/hawaii +0 -0
- data/data/nad.lst +142 -0
- data/data/nad27 +809 -0
- data/data/nad83 +744 -0
- data/data/ntf_r93.gsb +0 -0
- data/data/ntv1_can.dat +0 -0
- data/data/null +0 -0
- data/data/nzgd2kgrid0005.gsb +0 -0
- data/data/other.extra +53 -0
- data/data/proj_def.dat +17 -0
- data/data/prvi +0 -0
- data/data/stgeorge +0 -0
- data/data/stlrnc +0 -0
- data/data/stpaul +0 -0
- data/data/world +212 -0
- data/ffi-proj4.gemspec +23 -0
- data/lib/ffi-proj4.rb +164 -0
- data/lib/ffi-proj4/error.rb +92 -0
- data/lib/ffi-proj4/point.rb +39 -0
- data/lib/ffi-proj4/projection.rb +310 -0
- data/lib/ffi-proj4/projxy.rb +64 -0
- data/lib/ffi-proj4/tools.rb +57 -0
- data/lib/ffi-proj4/version.rb +5 -0
- data/test/point_tests.rb +69 -0
- data/test/projection_tests.rb +197 -0
- data/test/simple_transformation_tests.rb +82 -0
- data/test/test_helper.rb +87 -0
- data/test/transformation_tests.rb +108 -0
- metadata +108 -0
@@ -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
|