proj4r 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/rb_geod.c ADDED
@@ -0,0 +1,392 @@
1
+ #include "ruby.h"
2
+ #include "rb_proj4r.h"
3
+
4
+ #define MAX_PARGS 50
5
+
6
+ VALUE rb_cGeod;
7
+
8
+ static void
9
+ free_geod (Geod *geod)
10
+ {
11
+ if ( geod->ref ) {
12
+ free(geod->ref);
13
+ }
14
+ free(geod);
15
+ }
16
+
17
+ static VALUE
18
+ rb_geod_s_allocate (VALUE klass)
19
+ {
20
+ Geod *geod;
21
+ return Data_Make_Struct(klass, Geod, 0, free_geod, geod);
22
+ }
23
+
24
+
25
+ /*
26
+ * call-seq:
27
+ * Geod.new(a = 6378137, f = 1.0/298.257223563) -> nil
28
+ *
29
+ * Sets parameters for geodesic computations.
30
+ * See manual of 'geod' for the details of paramters.
31
+ *
32
+ * Geod.init("+ellps=WGS84 +units=km")
33
+ *
34
+ */
35
+
36
+
37
+ static VALUE
38
+ rb_geod_initialize (int argc, VALUE *argv, VALUE self)
39
+ {
40
+ VALUE ra, rf;
41
+ Geod *geod;
42
+ rb_scan_args(argc, argv, "02", &ra, &rf);
43
+ if ( NIL_P(ra) && NIL_P(rf) ) {
44
+ ra = rb_float_new(6378137); /* WGS84 : default */
45
+ rf = rb_float_new(1.0/298.257223563);
46
+ }
47
+ Data_Get_Struct(self, Geod, geod);
48
+ geod->ref = ALLOC(struct geod_geodesic);
49
+ geod_init(geod->ref, NUM2DBL(ra), NUM2DBL(rf));
50
+ return Qnil;
51
+ }
52
+
53
+ /*
54
+ * call-seq:
55
+ * Geod.invgeod(lon1,lat1,lon2,lat2) -> array
56
+ *
57
+ * Calculates the forward and back azimuths and distance
58
+ * between an initial and terminus point latitudes and longitudes.
59
+ * The latitude should be a floating point value in degrees, which
60
+ * positive value means northern direction. The longitude should be a
61
+ * floating point value in degrees, which positive value means western
62
+ * direction. The returned array contains the forward and back azimuths
63
+ * in degrees and distance in the metric specified by +Geod.init+.
64
+ *
65
+ * Geod.init("+ellps=WGS84 +units=km")
66
+ * Geod.invgeod(100,30,-100,-30)
67
+ * #=> [94.9053479513086, -85.0946520486914, 18101.7096098234]
68
+ *
69
+ */
70
+
71
+ static VALUE
72
+ rb_geod_inverse (VALUE self, VALUE vl1, VALUE vp1, VALUE vl2, VALUE vp2)
73
+ {
74
+ Geod *geod;
75
+ double ps12, paz12, paz21;
76
+ Data_Get_Struct(self, Geod, geod);
77
+ geod_inverse(geod->ref,
78
+ NUM2DBL(vl1),
79
+ NUM2DBL(vp1),
80
+ NUM2DBL(vl2),
81
+ NUM2DBL(vp2),
82
+ &ps12, &paz12, &paz21);
83
+ return rb_ary_new3(3,
84
+ rb_float_new(ps12),
85
+ rb_float_new(paz12),
86
+ rb_float_new(paz21));
87
+ }
88
+
89
+ /*
90
+ * call-seq:
91
+ * Geod.distance(lon1,lat1,lon2,lat2) -> array
92
+ *
93
+ * Shortcut of +Geod.invgeod(lon1,lat1,lon2,lat2)[2]+
94
+ *
95
+ */
96
+
97
+ static VALUE
98
+ rb_geod_distance (VALUE self, VALUE vl1, VALUE vp1, VALUE vl2, VALUE vp2)
99
+ {
100
+ Geod *geod;
101
+ double ps12;
102
+ Data_Get_Struct(self, Geod, geod);
103
+ geod_inverse(geod->ref,
104
+ NUM2DBL(vl1), NUM2DBL(vp1), NUM2DBL(vl2), NUM2DBL(vp2),
105
+ &ps12, 0, 0);
106
+ return rb_float_new(ps12);
107
+ }
108
+
109
+ /*
110
+ * call-seq:
111
+ * Geod.geod(lat1, lon1, az12, dist) -> array
112
+ *
113
+ * Calculates the latitude, longitude and back azimuth of a terminus point
114
+ * given a initial point latitude, longitude, azimuth and distance.
115
+ * The latitude should be a floating point value in degrees, which
116
+ * positive value means northern direction. The longitude should be a
117
+ * floating point value in degrees, which positive value means western
118
+ * direction. The returned array contains the latitude, longitude and
119
+ * back azimuth at the terminus point in degrees.
120
+ *
121
+ * Geod.init("+ellps=WGS84 +units=km")
122
+ * Geod.geod(100,30,94.9053479513086,18101.7096098234)
123
+ * #=> [-99.9999997537569, -30.0000001660067, -85.0946531598225]
124
+ *
125
+ */
126
+
127
+ static VALUE
128
+ rb_geod_forward (VALUE self, VALUE vlat1, VALUE vlon1, VALUE vaz, VALUE vdist)
129
+ {
130
+ Geod *geod;
131
+ double l2, p2, az21;
132
+ Data_Get_Struct(self, Geod, geod);
133
+ geod_direct(geod->ref,
134
+ NUM2DBL(vlat1), NUM2DBL(vlon1), NUM2DBL(vaz), NUM2DBL(vdist),
135
+ &l2, &p2, &az21);
136
+ return rb_ary_new3(3,
137
+ rb_float_new(l2),
138
+ rb_float_new(p2),
139
+ rb_float_new(az21));
140
+ }
141
+
142
+ #ifdef HAVE_CARRAY_H
143
+
144
+ #include "carray.h"
145
+
146
+
147
+ /*
148
+ * call-seq:
149
+ * Geod#direct_ca(lat1, lon1, az12, dist, lat2, lon2, az21) -> nil
150
+ * Geod#forward_ca(lat1, lon1, az12, dist, lat2, lon2, az21) -> nil
151
+ *
152
+ * Calculates the latitude, longitude and back azimuth of a terminus point
153
+ * given a initial point latitude, longitude, azimuth and distance.
154
+ * The latitude should be a floating point value in degrees, which
155
+ * positive value means northern direction. The longitude should be a
156
+ * floating point value in degrees, which positive value means western
157
+ * direction. The returned array contains the latitude, longitude and
158
+ * back azimuth at the terminus point in degrees.
159
+ *
160
+ * input:
161
+ *
162
+ * lat1, lon1, az12, dist
163
+ * read only CArray.double or its convertible
164
+ *
165
+ * output:
166
+ *
167
+ * lat2, lon2, az21
168
+ * writable CArray.double or its convertible
169
+ *
170
+ */
171
+
172
+ static VALUE
173
+ rb_geod_direct_ca (VALUE self,
174
+ volatile VALUE vlat1, volatile VALUE vlon1,
175
+ volatile VALUE vaz12, volatile VALUE vdist,
176
+ volatile VALUE vlat2, volatile VALUE vlon2,
177
+ volatile VALUE vaz21)
178
+ {
179
+ Geod *geod;
180
+ CArray *lat1, *lon1, *az12, *dist, *lat2, *lon2, *az21;
181
+ double *p1, *p2, *p3, *p4, *p5, *p6, *p7;
182
+ ca_size_t s1, s2, s3, s4, s5, s6, s7;
183
+ int8_t *m, *mp;
184
+ ca_size_t count;
185
+ ca_size_t i;
186
+
187
+ Data_Get_Struct(self, Geod, geod);
188
+
189
+ lat1 = ca_wrap_readonly(vlat1, CA_DOUBLE);
190
+ lon1 = ca_wrap_readonly(vlon1, CA_DOUBLE);
191
+ az12 = ca_wrap_readonly(vaz12, CA_DOUBLE);
192
+ dist = ca_wrap_readonly(vdist, CA_DOUBLE);
193
+ lat2 = ca_wrap_writable(vlat2, CA_DOUBLE);
194
+ lon2 = ca_wrap_writable(vlon2, CA_DOUBLE);
195
+ az21 = ca_wrap_writable(vaz21, CA_DOUBLE);
196
+
197
+ ca_attach_n(7, lat1, lon1, dist, az12, lat2, lon2, az21);
198
+
199
+ count = ca_set_iterator(7,
200
+ lat1, &p1, &s1,
201
+ lon1, &p2, &s2,
202
+ az12, &p3, &s3,
203
+ dist, &p4, &s4,
204
+ lat2, &p5, &s5,
205
+ lon2, &p6, &s6,
206
+ az21, &p7, &s7);
207
+
208
+ m = ca_allocate_mask_iterator(4, lat1, lon1, az12, dist);
209
+
210
+ mp = m;
211
+ for (i=0; i<count; i++) {
212
+ if ( *mp ) {
213
+ *p5 = 0.0/0.0;
214
+ *p6 = 0.0/0.0;
215
+ *p7 = 0.0/0.0;
216
+ }
217
+ else {
218
+ geod_direct(geod->ref, *p1, *p2, *p3, *p4, p5, p6, p7);
219
+ }
220
+ p1+=s1; p2+=s2; p3+=s3; p4+=s4; p5+=s5; p6+=s6; p7+=s7;
221
+ mp++;
222
+ }
223
+
224
+ free(m);
225
+
226
+ ca_sync_n(3, lat2, lon2, az21);
227
+ ca_detach_n(7, lat1, lon1, dist, az12, lat2, lon2, az21);
228
+
229
+ return Qnil;
230
+ }
231
+
232
+ /*
233
+ * call-seq:
234
+ * Geod.invgeod_d(lat1,lon1,lat2,lon2,dist,az12,az21) -> nil
235
+ *
236
+ * Calculates the forward and back azimuths and distance
237
+ * between an initial and terminus point latitudes and longitudes.
238
+ * The latitude should be a floating point value in degrees, which
239
+ * positive value means northern direction. The longitude should be a
240
+ * floating point value in degrees, which positive value means western
241
+ * direction. The returned array contains the forward and back azimuths
242
+ * in degrees and distance in the metric specified by +Geod.init+.
243
+ *
244
+ * input:
245
+ *
246
+ * lat1, lon1, lat2, lon2
247
+ * read only CArray.double or its convertible
248
+ *
249
+ * output:
250
+ *
251
+ * dist, az12, az21
252
+ * writable CArray.double or its convertible
253
+ *
254
+ */
255
+
256
+ static VALUE
257
+ rb_geod_inverse_ca (VALUE self,
258
+ volatile VALUE vlat1, volatile VALUE vlon1,
259
+ volatile VALUE vlat2, volatile VALUE vlon2,
260
+ volatile VALUE vdist, volatile VALUE vaz12,
261
+ volatile VALUE vaz21)
262
+ {
263
+ Geod *geod;
264
+ CArray *lat1, *lon1, *lat2, *lon2, *dist, *az12, *az21;
265
+ double *p1, *p2, *p3, *p4, *p5, *p6, *p7;
266
+ ca_size_t s1, s2, s3, s4, s5, s6, s7;
267
+ int8_t *m, *mp;
268
+ ca_size_t count;
269
+ ca_size_t i;
270
+
271
+ Data_Get_Struct(self, Geod, geod);
272
+
273
+ lat1 = ca_wrap_readonly(vlat1, CA_DOUBLE);
274
+ lon1 = ca_wrap_readonly(vlon1, CA_DOUBLE);
275
+ lat2 = ca_wrap_readonly(vlat2, CA_DOUBLE);
276
+ lon2 = ca_wrap_readonly(vlon2, CA_DOUBLE);
277
+ dist = ca_wrap_writable(vdist, CA_DOUBLE);
278
+ az12 = ca_wrap_writable(vaz12, CA_DOUBLE);
279
+ az21 = ca_wrap_writable(vaz21, CA_DOUBLE);
280
+
281
+ ca_attach_n(7, lat1, lon1, lat2, lon2, dist, az12, az21);
282
+
283
+ count = ca_set_iterator(7,
284
+ lat1, &p1, &s1,
285
+ lon1, &p2, &s2,
286
+ lat2, &p3, &s3,
287
+ lon2, &p4, &s4,
288
+ dist, &p5, &s5,
289
+ az12, &p6, &s6,
290
+ az21, &p7, &s7);
291
+
292
+ m = ca_allocate_mask_iterator(4, lat1, lon1, lat2, lon2);
293
+
294
+ mp = m;
295
+ for (i=0; i<count; i++) {
296
+ if ( *mp ) {
297
+ *p5 = 0.0/0.0;
298
+ *p6 = 0.0/0.0;
299
+ *p7 = 0.0/0.0;
300
+ }
301
+ else {
302
+ geod_inverse(geod->ref, *p1, *p2, *p3, *p4, p5, p6, p7);
303
+ }
304
+ p1+=s1; p2+=s2; p3+=s3; p4+=s4; p5+=s5; p6+=s6; p7+=s7;
305
+ mp++;
306
+ }
307
+
308
+ free(m);
309
+
310
+ ca_sync_n(3, dist, az12, az21);
311
+ ca_detach_n(7, lat1, lon1, lat2, lon2, dist, az12, az21);
312
+
313
+ return Qnil;
314
+ }
315
+
316
+ static VALUE
317
+ rb_geod_distance_ca (VALUE self,
318
+ volatile VALUE vlat1, volatile VALUE vlon1,
319
+ volatile VALUE vlat2, volatile VALUE vlon2,
320
+ volatile VALUE vdist)
321
+ {
322
+ Geod *geod;
323
+ CArray *lat1, *lon1, *lat2, *lon2, *dist;
324
+ double *p1, *p2, *p3, *p4, *p5;
325
+ double d0;
326
+ ca_size_t s1, s2, s3, s4, s5;
327
+ int8_t *m, *mp;
328
+ ca_size_t count;
329
+ ca_size_t i;
330
+
331
+ Data_Get_Struct(self, Geod, geod);
332
+
333
+ lat1 = ca_wrap_readonly(vlat1, CA_DOUBLE);
334
+ lon1 = ca_wrap_readonly(vlon1, CA_DOUBLE);
335
+ lat2 = ca_wrap_readonly(vlat2, CA_DOUBLE);
336
+ lon2 = ca_wrap_readonly(vlon2, CA_DOUBLE);
337
+ dist = ca_wrap_writable(vdist, CA_DOUBLE);
338
+
339
+ ca_attach_n(5, lat1, lon1, lat2, lon2, dist);
340
+
341
+ count = ca_set_iterator(5,
342
+ lat1, &p1, &s1,
343
+ lon1, &p2, &s2,
344
+ lat2, &p3, &s3,
345
+ lon2, &p4, &s4,
346
+ dist, &p5, &s5);
347
+
348
+ m = ca_allocate_mask_iterator(4, lat1, lon1, lat2, lon2);
349
+
350
+ mp = m;
351
+ for (i=0; i<count; i++) {
352
+ if ( *mp ) {
353
+ *p5 = 0.0/0.0;
354
+ }
355
+ else {
356
+ geod_inverse(geod->ref, *p1, *p2, *p3, *p4, &d0, 0, 0);
357
+ *p5 = d0;
358
+ }
359
+ p1+=s1; p2+=s2; p3+=s3; p4+=s4; p5+=s5;
360
+ mp++;
361
+ }
362
+
363
+ free(m);
364
+
365
+ ca_sync(dist);
366
+ ca_detach_n(5, lat1, lon1, lat2, lon2, dist);
367
+
368
+ return Qnil;
369
+ }
370
+
371
+
372
+ #endif
373
+
374
+ void
375
+ Init_geod ()
376
+ {
377
+ rb_cGeod = rb_define_class_under(rb_mPROJ4, "Geod", rb_cObject);
378
+ rb_define_alloc_func(rb_cGeod, rb_geod_s_allocate);
379
+ rb_define_method(rb_cGeod, "_initialize", rb_geod_initialize, -1);
380
+ rb_define_private_method(rb_cGeod, "_inverse", rb_geod_inverse, 4);
381
+ rb_define_private_method(rb_cGeod, "_distance", rb_geod_distance, 4);
382
+ rb_define_private_method(rb_cGeod, "_forward", rb_geod_forward, 4);
383
+
384
+ #ifdef HAVE_CARRAY_H
385
+ rb_define_private_method(rb_cGeod, "_direct_ca", rb_geod_direct_ca, 7);
386
+ rb_define_private_method(rb_cGeod, "_forward_ca", rb_geod_direct_ca, 7);
387
+ rb_define_private_method(rb_cGeod, "_inverse_ca", rb_geod_inverse_ca, 7);
388
+ rb_define_private_method(rb_cGeod, "_distance_ca", rb_geod_distance_ca, 5);
389
+ #endif
390
+
391
+ }
392
+