rgeo-proj4 2.0.1 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 05e81871744567d55f6b9d11e115954040f5262e9d3317b9792973f1806f655a
4
- data.tar.gz: 0a3f2943bf7c80eb4c9d0680b382ab0c9eed83543ef1f4383326e4bdfb6da250
3
+ metadata.gz: b578b226ebc3b376a92f58cf080d34f237b5929751e6b856e493d9b6a6daa1ae
4
+ data.tar.gz: 84f4953ac2aea3783e7a807542c7aaf102b265dc454a9de97c340d70f04685c1
5
5
  SHA512:
6
- metadata.gz: 74ad26d2f7f983b9415b1b301180fe0352a09d4df7425227c2d012220468e147d9b1787c41de2b13f2db9cb5bf9e8a35038beee362393427911854cbff4c49f8
7
- data.tar.gz: bcd150775bfa449dad65dd8035f9ceb4e23192dac1f258de2c0b3edf58362e6c48a4b7da7c710bcf3b43de61c92ca64b159f7b6eec2cdaa1ca0cda7a1a1a5ff0
6
+ metadata.gz: bb2bddfdc101da5c0a4b2af5d5445ce7e57f8d49505b5bb2f53376e81b5ed392d4d2183c11e6cc1e0d86fc5b2fc83cc1afe1d6a3c72fcf8924d779fbd04fdffb
7
+ data.tar.gz: 3f21328f3beff54191cf496f784a432185f9999d4b71ddeab91d772627f26bbc97deb1882cab9d9f997d16432993d2c742c3d2638005d2f372959428201f4e05
@@ -22,6 +22,7 @@ else
22
22
  "/opt/proj/include",
23
23
  "/opt/proj4/include",
24
24
  "/opt/include",
25
+ "/opt/homebrew/include",
25
26
  "/Library/Frameworks/PROJ.framework/unix/include",
26
27
  "/usr/include"
27
28
  ]
@@ -36,6 +37,7 @@ else
36
37
  "/opt/proj/lib",
37
38
  "/opt/proj4/lib",
38
39
  "/opt/lib",
40
+ "/opt/homebrew/lib",
39
41
  "/Library/Frameworks/PROJ.framework/unix/lib",
40
42
  "/usr/lib",
41
43
  "/usr/lib64"
@@ -45,15 +47,19 @@ else
45
47
 
46
48
  found_proj_ = false
47
49
  header_dirs_, lib_dirs_ = dir_config("proj", header_dirs_, lib_dirs_)
48
- dflag = "-DACCEPT_USE_OF_DEPRECATED_PROJ_API_H"
49
- if have_header("proj_api.h", nil, dflag)
50
+ if have_header("proj.h")
50
51
  $libs << " -lproj"
51
- if have_func("pj_init_plus", "proj_api.h", dflag)
52
+
53
+ if have_func("proj_create", "proj.h")
52
54
  found_proj_ = true
55
+ have_func("proj_create_crs_to_crs_from_pj", "proj.h")
56
+ have_func("proj_normalize_for_visualization", "proj.h")
53
57
  else
54
58
  $libs.gsub!(" -lproj", "")
55
59
  end
56
60
  end
61
+ have_func("rb_gc_mark_movable")
62
+
57
63
  unless found_proj_
58
64
  puts "**** WARNING: Unable to find Proj headers or Proj version is too old."
59
65
  puts "**** Compiling without Proj support."
@@ -1,12 +1,21 @@
1
1
  /*
2
2
  Main initializer for Proj4 wrapper
3
3
  */
4
- #ifdef HAVE_PROJ_API_H
5
- #ifdef HAVE_PJ_INIT_PLUS
6
- #define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H
4
+ #ifdef HAVE_PROJ_H
5
+ #ifdef HAVE_PROJ_CREATE
6
+ #ifdef HAVE_PROJ_CREATE_CRS_TO_CRS_FROM_PJ
7
+ #ifdef HAVE_PROJ_NORMALIZE_FOR_VISUALIZATION
7
8
  #define RGEO_PROJ4_SUPPORTED
8
9
  #endif
9
10
  #endif
11
+ #endif
12
+ #endif
13
+
14
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
15
+ #define mark rb_gc_mark_movable
16
+ #else
17
+ #define mark rb_gc_mark
18
+ #endif
10
19
 
11
20
  #ifdef __cplusplus
12
21
  #define RGEO_BEGIN_C extern "C" {
@@ -20,7 +29,7 @@
20
29
  #ifdef RGEO_PROJ4_SUPPORTED
21
30
 
22
31
  #include <ruby.h>
23
- #include <proj_api.h>
32
+ #include <proj.h>
24
33
 
25
34
  #endif
26
35
 
@@ -30,78 +39,159 @@ RGEO_BEGIN_C
30
39
 
31
40
  #ifdef RGEO_PROJ4_SUPPORTED
32
41
 
42
+ #if PROJ_VERSION_MAJOR == 6 && PROJ_VERSION_MINOR < 3
43
+ #define WKT_TYPE PJ_WKT2_2018
44
+ #else
45
+ #define WKT_TYPE PJ_WKT2_2019
46
+ #endif
33
47
 
34
48
  typedef struct {
35
- projPJ pj;
49
+ PJ *pj;
36
50
  VALUE original_str;
37
51
  char uses_radians;
38
52
  } RGeo_Proj4Data;
39
53
 
40
-
41
- #define RGEO_PROJ4_DATA_PTR(obj) ((RGeo_Proj4Data*)DATA_PTR(obj))
54
+ typedef struct {
55
+ PJ *crs_to_crs;
56
+ } RGeo_CRSToCRSData;
42
57
 
43
58
 
44
59
  // Destroy function for proj data.
60
+ static void rgeo_proj4_free(void *ptr)
61
+ {
62
+ RGeo_Proj4Data *data = (RGeo_Proj4Data *)ptr;
63
+ if(data->pj){
64
+ proj_destroy(data->pj);
65
+ }
66
+ free(data);
67
+ }
45
68
 
46
- static void destroy_proj4_func(RGeo_Proj4Data* data)
69
+ // Destroy function for crs_to_crs data.
70
+ static void rgeo_crs_to_crs_free(void *ptr)
47
71
  {
48
- if (data->pj) {
49
- pj_free(data->pj);
72
+ RGeo_CRSToCRSData *data = (RGeo_CRSToCRSData *)ptr;
73
+ if(data->crs_to_crs){
74
+ proj_destroy(data->crs_to_crs);
50
75
  }
51
76
  free(data);
52
77
  }
53
78
 
54
79
 
55
- static void mark_proj4_func(RGeo_Proj4Data* data)
80
+ static size_t rgeo_proj4_memsize(const void *ptr)
56
81
  {
57
- if (!NIL_P(data->original_str)) {
58
- rb_gc_mark(data->original_str);
82
+ size_t size = 0;
83
+ const RGeo_Proj4Data *data = (const RGeo_Proj4Data *)ptr;
84
+
85
+ size += sizeof(*data);
86
+ if(data->pj){
87
+ size += sizeof(data->pj);
59
88
  }
89
+ return size;
60
90
  }
61
91
 
92
+ static size_t rgeo_crs_to_crs_memsize(const void *ptr)
93
+ {
94
+ size_t size = 0;
95
+ const RGeo_CRSToCRSData *data = (const RGeo_CRSToCRSData *)ptr;
96
+ size += sizeof(*data);
97
+ if(data->crs_to_crs){
98
+ size += sizeof(data->crs_to_crs);
99
+ }
100
+ return size;
101
+ }
102
+
103
+ static void rgeo_proj4_mark(void *ptr)
104
+ {
105
+ RGeo_Proj4Data *data = (RGeo_Proj4Data *)ptr;
106
+ if(!NIL_P(data->original_str)){
107
+ mark(data->original_str);
108
+ }
109
+ }
110
+
111
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
112
+ static void rgeo_proj4_compact(void *ptr)
113
+ {
114
+ RGeo_Proj4Data *data = (RGeo_Proj4Data *)ptr;
115
+ if(data && !NIL_P(data->original_str)){
116
+ data->original_str = rb_gc_location(data->original_str);
117
+ }
118
+ }
119
+ #endif
62
120
 
63
- static VALUE alloc_proj4(VALUE klass)
121
+ static void rgeo_proj4_clear_struct(RGeo_Proj4Data *data)
122
+ {
123
+ if(data->pj){
124
+ proj_destroy(data->pj);
125
+ data->pj = NULL;
126
+ data->original_str = Qnil;
127
+ }
128
+ }
129
+
130
+ static const rb_data_type_t rgeo_proj4_data_type = {
131
+ "RGeo::CoordSys::Proj4",
132
+ {rgeo_proj4_mark, rgeo_proj4_free, rgeo_proj4_memsize,
133
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
134
+ rgeo_proj4_compact
135
+ #endif
136
+ },
137
+ 0, 0,
138
+ RUBY_TYPED_FREE_IMMEDIATELY};
139
+
140
+ static const rb_data_type_t rgeo_crs_to_crs_data_type = {
141
+ "RGeo::CoordSys::CRSToCRS",
142
+ {0, rgeo_crs_to_crs_free, rgeo_crs_to_crs_memsize},
143
+ 0, 0,
144
+ RUBY_TYPED_FREE_IMMEDIATELY};
145
+
146
+ static VALUE rgeo_proj4_data_alloc(VALUE self)
64
147
  {
65
148
  VALUE result;
66
- RGeo_Proj4Data* data;
149
+ RGeo_Proj4Data *data = ALLOC(RGeo_Proj4Data);
67
150
 
68
151
  result = Qnil;
69
- data = ALLOC(RGeo_Proj4Data);
70
- if (data) {
152
+
153
+ if(data){
71
154
  data->pj = NULL;
72
155
  data->original_str = Qnil;
73
156
  data->uses_radians = 0;
74
- result = Data_Wrap_Struct(klass, mark_proj4_func, destroy_proj4_func, data);
157
+ result = TypedData_Wrap_Struct(self, &rgeo_proj4_data_type, data);
75
158
  }
76
159
  return result;
77
160
  }
78
161
 
79
162
 
163
+ static VALUE rgeo_crs_to_crs_data_alloc(VALUE self)
164
+ {
165
+ VALUE result;
166
+ RGeo_CRSToCRSData *data = ALLOC(RGeo_CRSToCRSData);
167
+
168
+ result = Qnil;
169
+
170
+ if(data){
171
+ data->crs_to_crs = NULL;
172
+ result = TypedData_Wrap_Struct(self, &rgeo_crs_to_crs_data_type, data);
173
+ }
174
+ return result;
175
+ }
176
+
80
177
  static VALUE method_proj4_initialize_copy(VALUE self, VALUE orig)
81
178
  {
82
- RGeo_Proj4Data* self_data;
83
- projPJ pj;
84
- RGeo_Proj4Data* orig_data;
85
- char* str;
179
+ RGeo_Proj4Data *self_data;
180
+ RGeo_Proj4Data *orig_data;
181
+ const char* str;
86
182
 
87
183
  // Clear out any existing value
88
- self_data = RGEO_PROJ4_DATA_PTR(self);
89
- pj = self_data->pj;
90
- if (pj) {
91
- pj_free(pj);
92
- self_data->pj = NULL;
93
- self_data->original_str = Qnil;
94
- }
184
+ TypedData_Get_Struct(self, RGeo_Proj4Data, &rgeo_proj4_data_type, self_data);
185
+ rgeo_proj4_clear_struct(self_data);
95
186
 
96
187
  // Copy value from orig
97
- orig_data = RGEO_PROJ4_DATA_PTR(orig);
188
+ TypedData_Get_Struct(orig, RGeo_Proj4Data, &rgeo_proj4_data_type, orig_data);
98
189
  if (!NIL_P(orig_data->original_str)) {
99
- self_data->pj = pj_init_plus(RSTRING_PTR(orig_data->original_str));
190
+ self_data->pj = proj_create(PJ_DEFAULT_CTX, StringValuePtr(orig_data->original_str));
100
191
  }
101
192
  else {
102
- str = pj_get_def(orig_data->pj, 0);
103
- self_data->pj = pj_init_plus(str);
104
- pj_dalloc(str);
193
+ str = proj_as_proj_string(PJ_DEFAULT_CTX, orig_data->pj, PJ_PROJ_4, NULL);
194
+ self_data->pj = proj_create(PJ_DEFAULT_CTX, str);
105
195
  }
106
196
  self_data->original_str = orig_data->original_str;
107
197
  self_data->uses_radians = orig_data->uses_radians;
@@ -112,22 +202,16 @@ static VALUE method_proj4_initialize_copy(VALUE self, VALUE orig)
112
202
 
113
203
  static VALUE method_proj4_set_value(VALUE self, VALUE str, VALUE uses_radians)
114
204
  {
115
- RGeo_Proj4Data* self_data;
116
- projPJ pj;
205
+ RGeo_Proj4Data *self_data;
117
206
 
118
207
  Check_Type(str, T_STRING);
119
208
 
120
209
  // Clear out any existing value
121
- self_data = RGEO_PROJ4_DATA_PTR(self);
122
- pj = self_data->pj;
123
- if (pj) {
124
- pj_free(pj);
125
- self_data->pj = NULL;
126
- self_data->original_str = Qnil;
127
- }
210
+ TypedData_Get_Struct(self, RGeo_Proj4Data, &rgeo_proj4_data_type, self_data);
211
+ rgeo_proj4_clear_struct(self_data);
128
212
 
129
213
  // Set new data
130
- self_data->pj = pj_init_plus(RSTRING_PTR(str));
214
+ self_data->pj = proj_create(PJ_DEFAULT_CTX, StringValuePtr(str));
131
215
  self_data->original_str = str;
132
216
  self_data->uses_radians = RTEST(uses_radians) ? 1 : 0;
133
217
 
@@ -138,17 +222,18 @@ static VALUE method_proj4_set_value(VALUE self, VALUE str, VALUE uses_radians)
138
222
  static VALUE method_proj4_get_geographic(VALUE self)
139
223
  {
140
224
  VALUE result;
141
- RGeo_Proj4Data* new_data;
142
- RGeo_Proj4Data* self_data;
225
+ RGeo_Proj4Data *new_data;
226
+ RGeo_Proj4Data *self_data;
143
227
 
144
228
  result = Qnil;
145
229
  new_data = ALLOC(RGeo_Proj4Data);
146
230
  if (new_data) {
147
- self_data = RGEO_PROJ4_DATA_PTR(self);
148
- new_data->pj = pj_latlong_from_proj(self_data->pj);
231
+ TypedData_Get_Struct(self, RGeo_Proj4Data, &rgeo_proj4_data_type, self_data);
232
+
233
+ new_data->pj = proj_crs_get_geodetic_crs(PJ_DEFAULT_CTX, self_data->pj);
149
234
  new_data->original_str = Qnil;
150
235
  new_data->uses_radians = self_data->uses_radians;
151
- result = Data_Wrap_Struct(CLASS_OF(self), mark_proj4_func, destroy_proj4_func, new_data);
236
+ result = TypedData_Wrap_Struct(CLASS_OF(self), &rgeo_proj4_data_type, new_data);
152
237
  }
153
238
  return result;
154
239
  }
@@ -156,44 +241,97 @@ static VALUE method_proj4_get_geographic(VALUE self)
156
241
 
157
242
  static VALUE method_proj4_original_str(VALUE self)
158
243
  {
159
- return RGEO_PROJ4_DATA_PTR(self)->original_str;
244
+ RGeo_Proj4Data *data;
245
+ TypedData_Get_Struct(self, RGeo_Proj4Data, &rgeo_proj4_data_type, data);
246
+ return data->original_str;
160
247
  }
161
248
 
162
249
 
163
250
  static VALUE method_proj4_uses_radians(VALUE self)
164
251
  {
165
- return RGEO_PROJ4_DATA_PTR(self)->uses_radians ? Qtrue : Qfalse;
252
+ RGeo_Proj4Data *data;
253
+ TypedData_Get_Struct(self, RGeo_Proj4Data, &rgeo_proj4_data_type, data);
254
+ return data->uses_radians ? Qtrue : Qfalse;
166
255
  }
167
256
 
168
257
 
169
258
  static VALUE method_proj4_canonical_str(VALUE self)
170
259
  {
171
260
  VALUE result;
172
- projPJ pj;
173
- char* str;
261
+ PJ *pj;
262
+ const char *str;
263
+ RGeo_Proj4Data *data;
174
264
 
175
265
  result = Qnil;
176
- pj = RGEO_PROJ4_DATA_PTR(self)->pj;
266
+ TypedData_Get_Struct(self, RGeo_Proj4Data, &rgeo_proj4_data_type, data);
267
+ pj = data->pj;
177
268
  if (pj) {
178
- str = pj_get_def(pj, 0);
269
+ str = proj_as_proj_string(PJ_DEFAULT_CTX, pj, PJ_PROJ_4, NULL);
179
270
  if (str) {
180
271
  result = rb_str_new2(str);
181
- pj_dalloc(str);
182
272
  }
183
273
  }
184
274
  return result;
185
275
  }
186
276
 
277
+ static VALUE method_proj4_wkt_str(VALUE self)
278
+ {
279
+ VALUE result;
280
+ PJ *pj;
281
+ const char *str;
282
+ RGeo_Proj4Data *data;
283
+
284
+ result = Qnil;
285
+ TypedData_Get_Struct(self, RGeo_Proj4Data, &rgeo_proj4_data_type, data);
286
+ pj = data->pj;
287
+ if (pj) {
288
+ const char *const options[] = {"MULTILINE=NO", NULL};
289
+ str = proj_as_wkt(PJ_DEFAULT_CTX, pj, WKT_TYPE, options);
290
+ if(str){
291
+ result = rb_str_new2(str);
292
+ }
293
+ }
294
+ return result;
295
+ }
296
+
297
+ static VALUE method_proj4_auth_name_str(VALUE self)
298
+ {
299
+ VALUE result;
300
+ PJ *pj;
301
+ const char *id;
302
+ const char *auth;
303
+ RGeo_Proj4Data *data;
304
+
305
+ result = Qnil;
306
+ TypedData_Get_Struct(self, RGeo_Proj4Data, &rgeo_proj4_data_type, data);
307
+ pj = data->pj;
308
+ if (pj) {
309
+ auth = proj_get_id_auth_name(pj, 0);
310
+ id = proj_get_id_code(pj, 0);
311
+ if(id && auth){
312
+ result = rb_sprintf("%s:%s", auth, id);
313
+ }
314
+ }
315
+ return result;
316
+ }
187
317
 
188
318
  static VALUE method_proj4_is_geographic(VALUE self)
189
319
  {
190
320
  VALUE result;
191
- projPJ pj;
321
+ PJ *pj;
322
+ PJ_TYPE proj_type;
323
+ RGeo_Proj4Data *data;
192
324
 
193
325
  result = Qnil;
194
- pj = RGEO_PROJ4_DATA_PTR(self)->pj;
326
+ TypedData_Get_Struct(self, RGeo_Proj4Data, &rgeo_proj4_data_type, data);
327
+ pj = data->pj;
195
328
  if (pj) {
196
- result = pj_is_latlong(pj) ? Qtrue : Qfalse;
329
+ proj_type = proj_get_type(pj);
330
+ if(proj_type == PJ_TYPE_GEOGRAPHIC_2D_CRS || proj_type == PJ_TYPE_GEOGRAPHIC_3D_CRS){
331
+ result = Qtrue;
332
+ } else {
333
+ result = Qfalse;
334
+ }
197
335
  }
198
336
  return result;
199
337
  }
@@ -202,12 +340,16 @@ static VALUE method_proj4_is_geographic(VALUE self)
202
340
  static VALUE method_proj4_is_geocentric(VALUE self)
203
341
  {
204
342
  VALUE result;
205
- projPJ pj;
343
+ PJ *pj;
344
+ PJ_TYPE proj_type;
345
+ RGeo_Proj4Data *data;
206
346
 
207
347
  result = Qnil;
208
- pj = RGEO_PROJ4_DATA_PTR(self)->pj;
348
+ TypedData_Get_Struct(self, RGeo_Proj4Data, &rgeo_proj4_data_type, data);
349
+ pj = data->pj;
209
350
  if (pj) {
210
- result = pj_is_geocent(pj) ? Qtrue : Qfalse;
351
+ proj_type = proj_get_type(pj);
352
+ result = proj_type == PJ_TYPE_GEOCENTRIC_CRS ? Qtrue : Qfalse;
211
353
  }
212
354
  return result;
213
355
  }
@@ -215,89 +357,130 @@ static VALUE method_proj4_is_geocentric(VALUE self)
215
357
 
216
358
  static VALUE method_proj4_is_valid(VALUE self)
217
359
  {
218
- return RGEO_PROJ4_DATA_PTR(self)->pj ? Qtrue : Qfalse;
360
+ RGeo_Proj4Data *data;
361
+ TypedData_Get_Struct(self, RGeo_Proj4Data, &rgeo_proj4_data_type, data);
362
+ return data->pj ? Qtrue : Qfalse;
219
363
  }
220
364
 
221
365
 
222
366
  static VALUE cmethod_proj4_version(VALUE module)
223
367
  {
224
- return INT2NUM(PJ_VERSION);
368
+ return rb_sprintf("%d.%d.%d", PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR, PROJ_VERSION_PATCH);
225
369
  }
226
370
 
227
-
228
- static VALUE cmethod_proj4_transform(VALUE module, VALUE from, VALUE to, VALUE x, VALUE y, VALUE z)
371
+ static VALUE cmethod_proj4_create(VALUE klass, VALUE str, VALUE uses_radians)
229
372
  {
230
373
  VALUE result;
231
- projPJ from_pj;
232
- projPJ to_pj;
233
- double xval, yval, zval;
234
- int err;
374
+ RGeo_Proj4Data* data;
235
375
 
236
376
  result = Qnil;
237
- from_pj = RGEO_PROJ4_DATA_PTR(from)->pj;
238
- to_pj = RGEO_PROJ4_DATA_PTR(to)->pj;
239
- if (from_pj && to_pj) {
240
- xval = rb_num2dbl(x);
241
- yval = rb_num2dbl(y);
242
- zval = 0.0;
243
- if (!NIL_P(z)) {
244
- zval = rb_num2dbl(z);
245
- }
246
- err = pj_transform(from_pj, to_pj, 1, 1, &xval, &yval, NIL_P(z) ? NULL : &zval);
247
- if (!err && xval != HUGE_VAL && yval != HUGE_VAL && (NIL_P(z) || zval != HUGE_VAL)) {
248
- result = rb_ary_new2(NIL_P(z) ? 2 : 3);
249
- rb_ary_push(result, rb_float_new(xval));
250
- rb_ary_push(result, rb_float_new(yval));
251
- if (!NIL_P(z)) {
252
- rb_ary_push(result, rb_float_new(zval));
253
- }
254
- }
377
+ Check_Type(str, T_STRING);
378
+ data = ALLOC(RGeo_Proj4Data);
379
+ if (data) {
380
+ data->pj = proj_create(PJ_DEFAULT_CTX, StringValuePtr(str));
381
+ data->original_str = str;
382
+ data->uses_radians = RTEST(uses_radians) ? 1 : 0;
383
+ result = TypedData_Wrap_Struct(klass, &rgeo_proj4_data_type, data);
384
+ }
385
+ return result;
386
+ }
387
+
388
+ static VALUE cmethod_crs_to_crs_create(VALUE klass, VALUE from, VALUE to)
389
+ {
390
+ VALUE result;
391
+ RGeo_Proj4Data *from_data;
392
+ RGeo_Proj4Data *to_data;
393
+ result = Qnil;
394
+ PJ *from_pj;
395
+ PJ *to_pj;
396
+ PJ *gis_pj;
397
+ PJ *crs_to_crs;
398
+ RGeo_CRSToCRSData* data;
399
+
400
+ TypedData_Get_Struct(from, RGeo_Proj4Data, &rgeo_proj4_data_type, from_data);
401
+ TypedData_Get_Struct(to, RGeo_Proj4Data, &rgeo_proj4_data_type, to_data);
402
+ from_pj = from_data->pj;
403
+ to_pj = to_data->pj;
404
+ crs_to_crs = proj_create_crs_to_crs_from_pj(PJ_DEFAULT_CTX, from_pj, to_pj, 0, NULL);
405
+
406
+ // necessary to use proj_normalize_for_visualization so that we
407
+ // do not have to worry about the order of coordinates in every
408
+ // coord system
409
+ gis_pj = proj_normalize_for_visualization(PJ_DEFAULT_CTX, crs_to_crs);
410
+ if(gis_pj){
411
+ proj_destroy(crs_to_crs);
412
+ crs_to_crs = gis_pj;
413
+ }
414
+ data = ALLOC(RGeo_CRSToCRSData);
415
+ if (data){
416
+ data->crs_to_crs = crs_to_crs;
417
+ result = TypedData_Wrap_Struct(klass, &rgeo_crs_to_crs_data_type, data);
255
418
  }
256
419
  return result;
257
420
  }
258
421
 
259
422
 
260
- static VALUE cmethod_proj4_create(VALUE klass, VALUE str, VALUE uses_radians)
423
+ static VALUE method_crs_to_crs_transform(VALUE self, VALUE x, VALUE y, VALUE z)
261
424
  {
262
425
  VALUE result;
263
- RGeo_Proj4Data* data;
426
+ RGeo_CRSToCRSData *crs_to_crs_data;
427
+ PJ *crs_to_crs_pj;
428
+ double xval, yval, zval;
429
+ PJ_COORD input;
430
+ PJ_COORD output;
264
431
 
265
432
  result = Qnil;
266
- Check_Type(str, T_STRING);
267
- data = ALLOC(RGeo_Proj4Data);
268
- if (data) {
269
- data->pj = pj_init_plus(RSTRING_PTR(str));
270
- data->original_str = str;
271
- data->uses_radians = RTEST(uses_radians) ? 1 : 0;
272
- result = Data_Wrap_Struct(klass, mark_proj4_func, destroy_proj4_func, data);
433
+ TypedData_Get_Struct(self, RGeo_CRSToCRSData, &rgeo_crs_to_crs_data_type, crs_to_crs_data);
434
+ crs_to_crs_pj = crs_to_crs_data->crs_to_crs;
435
+ if(crs_to_crs_pj){
436
+ xval = rb_num2dbl(x);
437
+ yval = rb_num2dbl(y);
438
+ zval = NIL_P(z) ? 0.0 : rb_num2dbl(z);
439
+
440
+ input = proj_coord(xval, yval, zval, HUGE_VAL);
441
+ output = proj_trans(crs_to_crs_pj, PJ_FWD, input);
442
+
443
+ result = rb_ary_new2(NIL_P(z) ? 2 : 3);
444
+ rb_ary_push(result, DBL2NUM(output.xyz.x));
445
+ rb_ary_push(result, DBL2NUM(output.xyz.y));
446
+ if(!NIL_P(z)){
447
+ rb_ary_push(result, DBL2NUM(output.xyz.z));
448
+ }
273
449
  }
274
450
  return result;
275
451
  }
276
452
 
277
-
278
453
  static void rgeo_init_proj4()
279
454
  {
280
455
  VALUE rgeo_module;
281
456
  VALUE coordsys_module;
282
457
  VALUE proj4_class;
458
+ VALUE crs_to_crs_class;
283
459
 
284
460
  rgeo_module = rb_define_module("RGeo");
285
461
  coordsys_module = rb_define_module_under(rgeo_module, "CoordSys");
286
- proj4_class = rb_define_class_under(coordsys_module, "Proj4", rb_cObject);
287
462
 
288
- rb_define_alloc_func(proj4_class, alloc_proj4);
463
+ proj4_class = rb_define_class_under(coordsys_module, "Proj4", rb_cObject);
464
+ rb_define_alloc_func(proj4_class, rgeo_proj4_data_alloc);
289
465
  rb_define_module_function(proj4_class, "_create", cmethod_proj4_create, 2);
290
466
  rb_define_method(proj4_class, "initialize_copy", method_proj4_initialize_copy, 1);
291
467
  rb_define_method(proj4_class, "_set_value", method_proj4_set_value, 2);
292
468
  rb_define_method(proj4_class, "_original_str", method_proj4_original_str, 0);
293
469
  rb_define_method(proj4_class, "_canonical_str", method_proj4_canonical_str, 0);
470
+ rb_define_method(proj4_class, "_as_text", method_proj4_wkt_str, 0);
471
+ rb_define_method(proj4_class, "_auth_name", method_proj4_auth_name_str, 0);
294
472
  rb_define_method(proj4_class, "_valid?", method_proj4_is_valid, 0);
295
473
  rb_define_method(proj4_class, "_geographic?", method_proj4_is_geographic, 0);
296
474
  rb_define_method(proj4_class, "_geocentric?", method_proj4_is_geocentric, 0);
297
475
  rb_define_method(proj4_class, "_radians?", method_proj4_uses_radians, 0);
298
476
  rb_define_method(proj4_class, "_get_geographic", method_proj4_get_geographic, 0);
299
- rb_define_module_function(proj4_class, "_transform_coords", cmethod_proj4_transform, 5);
300
477
  rb_define_module_function(proj4_class, "_proj_version", cmethod_proj4_version, 0);
478
+
479
+
480
+ crs_to_crs_class = rb_define_class_under(coordsys_module, "CRSToCRS", rb_cObject);
481
+ rb_define_alloc_func(crs_to_crs_class, rgeo_crs_to_crs_data_alloc);
482
+ rb_define_module_function(crs_to_crs_class, "_create", cmethod_crs_to_crs_create, 2);
483
+ rb_define_method(crs_to_crs_class, "_transform_coords", method_crs_to_crs_transform, 3);
301
484
  }
302
485
 
303
486
 
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "singleton"
4
+ module RGeo
5
+ module CoordSys
6
+ # This is a Ruby wrapper around a proj crs_to_crs
7
+ # A crs_to_crs transformation object is a pipeline between two known coordinate reference systems.
8
+ # https://proj.org/development/reference/functions.html#c.proj_create_crs_to_crs
9
+ class CRSToCRS
10
+ attr_writer :from, :to
11
+
12
+ class << self
13
+ def create(from, to)
14
+ crs_to_crs = _create(from, to)
15
+ crs_to_crs.from = from
16
+ crs_to_crs.to = to
17
+ crs_to_crs
18
+ end
19
+ end
20
+ # transform the coordinates from the initial CRS to the destination CRS
21
+ def transform_coords(x, y, z)
22
+ if @from._radians? && @from._geographic?
23
+ x *= ImplHelper::Math::DEGREES_PER_RADIAN
24
+ y *= ImplHelper::Math::DEGREES_PER_RADIAN
25
+ end
26
+ result = _transform_coords(x, y, z)
27
+ if result && @to._radians? && @to._geographic?
28
+ result[0] *= ImplHelper::Math::RADIANS_PER_DEGREE
29
+ result[1] *= ImplHelper::Math::RADIANS_PER_DEGREE
30
+ end
31
+ result
32
+ end
33
+
34
+ def transform(from_geometry, to_factory)
35
+ case from_geometry
36
+ when Feature::Point
37
+ transform_point(from_geometry, to_factory)
38
+ when Feature::Line
39
+ to_factory.line(from_geometry.points.map { |p| transform_point(p, to_factory) })
40
+ when Feature::LinearRing
41
+ transform_linear_ring(from_geometry, to_factory)
42
+ when Feature::LineString
43
+ to_factory.line_string(from_geometry.points.map { |p_| transform_point(p_, to_factory) })
44
+ when Feature::Polygon
45
+ transform_polygon(from_geometry, to_factory)
46
+ when Feature::MultiPoint
47
+ to_factory.multi_point(from_geometry.map { |p| transform_point(p, to_factory) })
48
+ when Feature::MultiLineString
49
+ to_factory.multi_line_string(from_geometry.map { |g| transform(g, to_factory) })
50
+ when Feature::MultiPolygon
51
+ to_factory.multi_polygon(from_geometry.map { |p| transform_polygon(p, to_factory) })
52
+ when Feature::GeometryCollection
53
+ to_factory.collection(from_geometry.map { |g| transform(g, to_factory) })
54
+ end
55
+ end
56
+
57
+ def transform_point(from_point, to_factory)
58
+ from_factory_ = from_point.factory
59
+ from_has_z_ = from_factory_.property(:has_z_coordinate)
60
+ from_has_m_ = from_factory_.property(:has_m_coordinate)
61
+ to_has_z_ = to_factory.property(:has_z_coordinate)
62
+ to_has_m_ = to_factory.property(:has_m_coordinate)
63
+ coords_ = transform_coords(from_point.x, from_point.y, from_has_z_ ? from_point.z : nil)
64
+ return unless coords_
65
+ extras_ = []
66
+ extras_ << coords_[2].to_f if to_has_z_
67
+ if to_has_m_
68
+ extras_ << from_has_m_ ? from_point.m : 0.0
69
+ end
70
+ to_factory.point(coords_[0], coords_[1], *extras_)
71
+ end
72
+
73
+ def transform_linear_ring(from_ring_, to_factory_)
74
+ to_factory_.linear_ring(from_ring_.points[0..-2].map { |p_| transform_point(p_, to_factory_) })
75
+ end
76
+
77
+ def transform_polygon(from_polygon_, to_factory_)
78
+ ext_ = transform_linear_ring(from_polygon_.exterior_ring, to_factory_)
79
+ int_ = from_polygon_.interior_rings.map { |r_| transform_linear_ring(r_, to_factory_) }
80
+ to_factory_.polygon(ext_, int_)
81
+ end
82
+ end
83
+
84
+ # Store of all the created CRSToCRS
85
+ class CRSStore
86
+ include Singleton
87
+ class << self
88
+ def get(from, to)
89
+ instance.get(from, to)
90
+ end
91
+ end
92
+
93
+ Key = Struct.new(:from, :to)
94
+
95
+ def initialize
96
+ @store = Hash.new { |h, k| h[k] = CRSToCRS.create(k.from, k.to) }
97
+ @semaphore = Mutex.new
98
+ end
99
+
100
+ def get(from, to)
101
+ @semaphore.synchronize do
102
+ @store[Key.new(from, to)]
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -41,8 +41,8 @@ module RGeo
41
41
  # there are sometimes multiple ways to express a given coordinate
42
42
  # system.
43
43
 
44
- def eql?(rhs_)
45
- rhs_.class == self.class && rhs_.canonical_hash == canonical_hash && rhs_._radians? == _radians?
44
+ def eql?(other)
45
+ other.class == self.class && other.canonical_hash == canonical_hash && other._radians? == _radians?
46
46
  end
47
47
  alias == eql?
48
48
 
@@ -105,6 +105,21 @@ module RGeo
105
105
  _original_str
106
106
  end
107
107
 
108
+ # Returns the WKT representation of the CRS.
109
+
110
+ def as_text
111
+ _as_text
112
+ end
113
+
114
+ # Returns the string representing the authority and code of the
115
+ # CRS if it exists, nil otherwise.
116
+ #
117
+ # Ex. EPSG:4326
118
+
119
+ def auth_name
120
+ _auth_name
121
+ end
122
+
108
123
  # Returns true if this Proj4 object is a geographic (lat-long)
109
124
  # coordinate system.
110
125
 
@@ -172,7 +187,7 @@ module RGeo
172
187
  if defn_.is_a?(::Hash)
173
188
  defn_ = defn_.map { |k_, v_| v_ ? "+#{k_}=#{v_}" : "+#{k_}" }.join(" ")
174
189
  end
175
- defn_ = defn_.sub(/^(\s*)/, '\1+').gsub(/(\s+)([^+\s])/, '\1+\2') unless defn_ =~ /^\s*\+/
190
+
176
191
  result_ = _create(defn_, opts_[:radians])
177
192
  result_ = nil unless result_._valid?
178
193
  end
@@ -206,18 +221,9 @@ module RGeo
206
221
  # Transforms the given coordinate (x, y, [z]) from one proj4
207
222
  # coordinate system to another. Returns an array with either two
208
223
  # or three elements.
209
-
210
224
  def transform_coords(from_proj_, to_proj_, x_, y_, z_ = nil)
211
- if !from_proj_._radians? && from_proj_._geographic?
212
- x_ *= ImplHelper::Math::RADIANS_PER_DEGREE
213
- y_ *= ImplHelper::Math::RADIANS_PER_DEGREE
214
- end
215
- result_ = _transform_coords(from_proj_, to_proj_, x_, y_, z_)
216
- if result_ && !to_proj_._radians? && to_proj_._geographic?
217
- result_[0] *= ImplHelper::Math::DEGREES_PER_RADIAN
218
- result_[1] *= ImplHelper::Math::DEGREES_PER_RADIAN
219
- end
220
- result_
225
+ crs_to_crs = CRSStore.get(from_proj_, to_proj_)
226
+ crs_to_crs.transform_coords(x_, y_, z_)
221
227
  end
222
228
 
223
229
  # Low-level geometry transform method.
@@ -225,66 +231,9 @@ module RGeo
225
231
  # The resulting geometry is constructed using the to_factory.
226
232
  # Any projections associated with the factories themselves are
227
233
  # ignored.
228
-
229
234
  def transform(from_proj_, from_geometry_, to_proj_, to_factory_)
230
- case from_geometry_
231
- when Feature::Point
232
- transform_point(from_proj_, from_geometry_, to_proj_, to_factory_)
233
- when Feature::Line
234
- to_factory_.line(from_geometry_.points.map { |p_| transform_point(from_proj_, p_, to_proj_, to_factory_) })
235
- when Feature::LinearRing
236
- transform_linear_ring(from_proj_, from_geometry_, to_proj_, to_factory_)
237
- when Feature::LineString
238
- to_factory_.line_string(from_geometry_.points.map { |p_| transform_point(from_proj_, p_, to_proj_, to_factory_) })
239
- when Feature::Polygon
240
- transform_polygon(from_proj_, from_geometry_, to_proj_, to_factory_)
241
- when Feature::MultiPoint
242
- to_factory_.multi_point(from_geometry_.map { |p_| transform_point(from_proj_, p_, to_proj_, to_factory_) })
243
- when Feature::MultiLineString
244
- to_factory_.multi_line_string(from_geometry_.map { |g_| transform(from_proj_, g_, to_proj_, to_factory_) })
245
- when Feature::MultiPolygon
246
- to_factory_.multi_polygon(from_geometry_.map { |p_| transform_polygon(from_proj_, p_, to_proj_, to_factory_) })
247
- when Feature::GeometryCollection
248
- to_factory_.collection(from_geometry_.map { |g_| transform(from_proj_, g_, to_proj_, to_factory_) })
249
- end
250
- end
251
-
252
- private
253
-
254
- def transform_point(from_proj_, from_point_, to_proj_, to_factory_)
255
- from_factory_ = from_point_.factory
256
- from_has_z_ = from_factory_.property(:has_z_coordinate)
257
- from_has_m_ = from_factory_.property(:has_m_coordinate)
258
- to_has_z_ = to_factory_.property(:has_z_coordinate)
259
- to_has_m_ = to_factory_.property(:has_m_coordinate)
260
- x_ = from_point_.x
261
- y_ = from_point_.y
262
- if !from_proj_._radians? && from_proj_._geographic?
263
- x_ *= ImplHelper::Math::RADIANS_PER_DEGREE
264
- y_ *= ImplHelper::Math::RADIANS_PER_DEGREE
265
- end
266
- coords_ = _transform_coords(from_proj_, to_proj_, x_, y_, from_has_z_ ? from_point_.z : nil)
267
- return unless coords_
268
- if !to_proj_._radians? && to_proj_._geographic?
269
- coords_[0] *= ImplHelper::Math::DEGREES_PER_RADIAN
270
- coords_[1] *= ImplHelper::Math::DEGREES_PER_RADIAN
271
- end
272
- extras_ = []
273
- extras_ << coords_[2].to_f if to_has_z_
274
- if to_has_m_
275
- extras_ << from_has_m_ ? from_point_.m : 0.0
276
- end
277
- to_factory_.point(coords_[0], coords_[1], *extras_)
278
- end
279
-
280
- def transform_linear_ring(from_proj_, from_ring_, to_proj_, to_factory_)
281
- to_factory_.linear_ring(from_ring_.points[0..-2].map { |p_| transform_point(from_proj_, p_, to_proj_, to_factory_) })
282
- end
283
-
284
- def transform_polygon(from_proj_, from_polygon_, to_proj_, to_factory_)
285
- ext_ = transform_linear_ring(from_proj_, from_polygon_.exterior_ring, to_proj_, to_factory_)
286
- int_ = from_polygon_.interior_rings.map { |r_| transform_linear_ring(from_proj_, r_, to_proj_, to_factory_) }
287
- to_factory_.polygon(ext_, int_)
235
+ crs_to_crs = CRSStore.get(from_proj_, to_proj_)
236
+ crs_to_crs.transform(from_geometry_, to_factory_)
288
237
  end
289
238
  end
290
239
  end
@@ -81,11 +81,12 @@ module RGeo
81
81
  ident_ = ident_.to_s
82
82
  return @cache[ident_] if @cache&.include?(ident_)
83
83
  result_ = nil
84
- if @populate_state == 0
84
+ case @populate_state
85
+ when 0
85
86
  data_ = search_file(ident_)
86
87
  result_ = Entry.new(ident_, authority: @authority, authority_code: @authority ? ident_ : nil, name: data_[1], proj4: data_[2]) if data_
87
88
  @cache[ident_] = result_ if @cache
88
- elsif @populate_state == 1
89
+ when 1
89
90
  search_file(nil)
90
91
  result_ = @cache[ident_]
91
92
  @populate_state = 2
@@ -113,12 +114,10 @@ module RGeo
113
114
  cur_name_ = line_[comment_delim_ + 1..-1].strip
114
115
  line_ = line_[0..comment_delim_ - 1].strip
115
116
  end
116
- unless cur_ident_
117
- if line_ =~ /^<(\w+)>(.*)/
118
- cur_ident_ = Regexp.last_match(1)
119
- cur_text_ = []
120
- line_ = Regexp.last_match(2).strip
121
- end
117
+ if !cur_ident_ && (line_ =~ /^<(\w+)>(.*)/)
118
+ cur_ident_ = Regexp.last_match(1)
119
+ cur_text_ = []
120
+ line_ = Regexp.last_match(2).strip
122
121
  end
123
122
  next unless cur_ident_
124
123
  if line_[-2..-1] == "<>"
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RGeo
4
4
  module Proj4
5
- VERSION = "2.0.1"
5
+ VERSION = "3.1.1"
6
6
  end
7
7
  end
data/lib/rgeo/proj4.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "rgeo"
4
4
  require "rgeo/proj4/version"
5
+ require "rgeo/coord_sys/crs_to_crs"
5
6
  require "rgeo/coord_sys/proj4"
6
7
  require "rgeo/coord_sys/srs_database/proj4_data"
7
8
  require "rgeo/coord_sys/proj4_c_impl"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rgeo-proj4
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 3.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tee Parham, Daniel Azuma
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-09 00:00:00.000000000 Z
11
+ date: 2021-11-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rgeo
@@ -30,28 +30,42 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '5.11'
33
+ version: '5.14'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '5.11'
40
+ version: '5.14'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry-byebug
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.9.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 3.9.0
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - "~>"
46
60
  - !ruby/object:Gem::Version
47
- version: '12.0'
61
+ version: '13.0'
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: '12.0'
68
+ version: '13.0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rake-compiler
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +80,20 @@ dependencies:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
82
  version: '1.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 1.8.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 1.8.1
69
97
  description: Proj4 extension for rgeo.
70
98
  email:
71
99
  - parhameter@gmail.com, dazuma@gmail.com
@@ -77,6 +105,7 @@ files:
77
105
  - LICENSE.txt
78
106
  - ext/proj4_c_impl/extconf.rb
79
107
  - ext/proj4_c_impl/main.c
108
+ - lib/rgeo/coord_sys/crs_to_crs.rb
80
109
  - lib/rgeo/coord_sys/proj4.rb
81
110
  - lib/rgeo/coord_sys/srs_database/proj4_data.rb
82
111
  - lib/rgeo/proj4.rb
@@ -93,14 +122,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
93
122
  requirements:
94
123
  - - ">="
95
124
  - !ruby/object:Gem::Version
96
- version: 2.3.0
125
+ version: 2.5.0
97
126
  required_rubygems_version: !ruby/object:Gem::Requirement
98
127
  requirements:
99
128
  - - ">="
100
129
  - !ruby/object:Gem::Version
101
130
  version: '0'
102
131
  requirements: []
103
- rubygems_version: 3.0.3
132
+ rubygems_version: 3.1.4
104
133
  signing_key:
105
134
  specification_version: 4
106
135
  summary: Proj4 extension for rgeo.