rgeo-proj4 1.0.0.rc1
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 +7 -0
- data/LICENSE.txt +21 -0
- data/ext/proj4_c_impl/extconf.rb +62 -0
- data/ext/proj4_c_impl/main.c +315 -0
- data/lib/rgeo/coord_sys/proj4.rb +293 -0
- data/lib/rgeo/coord_sys/srs_database/proj4_data.rb +140 -0
- data/lib/rgeo/proj4.rb +5 -0
- data/lib/rgeo/proj4/version.rb +5 -0
- metadata +122 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b9c7651625154609a01979271df45bd5229be4c1c78b10834540981408bf8240
|
4
|
+
data.tar.gz: 53a6f93a200983b08020f8bea256c575a27e061b78f172a03d51caba53852442
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 24b00ed0eb17c8e77ea5fa1efd0eedc91468c6c79ee89d06ee0940cd613a3c382942e2f881c42d66a00281c3653f566d94524a05af6de388021631e06e04a376
|
7
|
+
data.tar.gz: 16e019ec27581cc9bca711beca05558339c43a2e919847861be79e96fd4a8c88cf592a3ddf91490dc47ab06099c1508cb26f695d3a535759371ae325fdb0788b
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Tee Parham, Daniel Azuma
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Makefile builder for Proj4 wrapper
|
4
|
+
#
|
5
|
+
# -----------------------------------------------------------------------------
|
6
|
+
|
7
|
+
if ::RUBY_DESCRIPTION =~ /^jruby\s/
|
8
|
+
|
9
|
+
::File.open("Makefile", "w") { |f_| f_.write(".PHONY: install\ninstall:\n") }
|
10
|
+
|
11
|
+
else
|
12
|
+
|
13
|
+
require "mkmf"
|
14
|
+
|
15
|
+
header_dirs_ =
|
16
|
+
[
|
17
|
+
::RbConfig::CONFIG["includedir"],
|
18
|
+
"/usr/local/include",
|
19
|
+
"/usr/local/proj/include",
|
20
|
+
"/usr/local/proj4/include",
|
21
|
+
"/opt/local/include",
|
22
|
+
"/opt/proj/include",
|
23
|
+
"/opt/proj4/include",
|
24
|
+
"/opt/include",
|
25
|
+
"/Library/Frameworks/PROJ.framework/unix/include",
|
26
|
+
"/usr/include"
|
27
|
+
]
|
28
|
+
lib_dirs_ =
|
29
|
+
[
|
30
|
+
::RbConfig::CONFIG["libdir"],
|
31
|
+
"/usr/local/lib",
|
32
|
+
"/usr/local/lib64",
|
33
|
+
"/usr/local/proj/lib",
|
34
|
+
"/usr/local/proj4/lib",
|
35
|
+
"/opt/local/lib",
|
36
|
+
"/opt/proj/lib",
|
37
|
+
"/opt/proj4/lib",
|
38
|
+
"/opt/lib",
|
39
|
+
"/Library/Frameworks/PROJ.framework/unix/lib",
|
40
|
+
"/usr/lib",
|
41
|
+
"/usr/lib64"
|
42
|
+
]
|
43
|
+
header_dirs_.delete_if { |path_| !::File.directory?(path_) }
|
44
|
+
lib_dirs_.delete_if { |path_| !::File.directory?(path_) }
|
45
|
+
|
46
|
+
found_proj_ = false
|
47
|
+
header_dirs_, lib_dirs_ = dir_config("proj", header_dirs_, lib_dirs_)
|
48
|
+
if have_header("proj_api.h")
|
49
|
+
$libs << " -lproj"
|
50
|
+
if have_func("pj_init_plus", "proj_api.h")
|
51
|
+
found_proj_ = true
|
52
|
+
else
|
53
|
+
$libs.gsub!(" -lproj", "")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
unless found_proj_
|
57
|
+
puts "**** WARNING: Unable to find Proj headers or Proj version is too old."
|
58
|
+
puts "**** Compiling without Proj support."
|
59
|
+
end
|
60
|
+
create_makefile("rgeo/coord_sys/proj4_c_impl")
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,315 @@
|
|
1
|
+
/*
|
2
|
+
Main initializer for Proj4 wrapper
|
3
|
+
*/
|
4
|
+
|
5
|
+
#ifdef HAVE_PROJ_API_H
|
6
|
+
#ifdef HAVE_PJ_INIT_PLUS
|
7
|
+
#define RGEO_PROJ4_SUPPORTED
|
8
|
+
#endif
|
9
|
+
#endif
|
10
|
+
|
11
|
+
#ifdef __cplusplus
|
12
|
+
#define RGEO_BEGIN_C extern "C" {
|
13
|
+
#define RGEO_END_C }
|
14
|
+
#else
|
15
|
+
#define RGEO_BEGIN_C
|
16
|
+
#define RGEO_END_C
|
17
|
+
#endif
|
18
|
+
|
19
|
+
|
20
|
+
#ifdef RGEO_PROJ4_SUPPORTED
|
21
|
+
|
22
|
+
#include <ruby.h>
|
23
|
+
#include <proj_api.h>
|
24
|
+
|
25
|
+
#endif
|
26
|
+
|
27
|
+
|
28
|
+
RGEO_BEGIN_C
|
29
|
+
|
30
|
+
|
31
|
+
#ifdef RGEO_PROJ4_SUPPORTED
|
32
|
+
|
33
|
+
|
34
|
+
typedef struct {
|
35
|
+
projPJ pj;
|
36
|
+
VALUE original_str;
|
37
|
+
char uses_radians;
|
38
|
+
} RGeo_Proj4Data;
|
39
|
+
|
40
|
+
|
41
|
+
#define RGEO_PROJ4_DATA_PTR(obj) ((RGeo_Proj4Data*)DATA_PTR(obj))
|
42
|
+
|
43
|
+
|
44
|
+
// Destroy function for proj data.
|
45
|
+
|
46
|
+
static void destroy_proj4_func(RGeo_Proj4Data* data)
|
47
|
+
{
|
48
|
+
if (data->pj) {
|
49
|
+
pj_free(data->pj);
|
50
|
+
}
|
51
|
+
free(data);
|
52
|
+
}
|
53
|
+
|
54
|
+
|
55
|
+
static void mark_proj4_func(RGeo_Proj4Data* data)
|
56
|
+
{
|
57
|
+
if (!NIL_P(data->original_str)) {
|
58
|
+
rb_gc_mark(data->original_str);
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
|
63
|
+
static VALUE alloc_proj4(VALUE klass)
|
64
|
+
{
|
65
|
+
VALUE result;
|
66
|
+
RGeo_Proj4Data* data;
|
67
|
+
|
68
|
+
result = Qnil;
|
69
|
+
data = ALLOC(RGeo_Proj4Data);
|
70
|
+
if (data) {
|
71
|
+
data->pj = NULL;
|
72
|
+
data->original_str = Qnil;
|
73
|
+
data->uses_radians = 0;
|
74
|
+
result = Data_Wrap_Struct(klass, mark_proj4_func, destroy_proj4_func, data);
|
75
|
+
}
|
76
|
+
return result;
|
77
|
+
}
|
78
|
+
|
79
|
+
|
80
|
+
static VALUE method_proj4_initialize_copy(VALUE self, VALUE orig)
|
81
|
+
{
|
82
|
+
RGeo_Proj4Data* self_data;
|
83
|
+
projPJ pj;
|
84
|
+
RGeo_Proj4Data* orig_data;
|
85
|
+
char* str;
|
86
|
+
|
87
|
+
// 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
|
+
}
|
95
|
+
|
96
|
+
// Copy value from orig
|
97
|
+
orig_data = RGEO_PROJ4_DATA_PTR(orig);
|
98
|
+
if (!NIL_P(orig_data->original_str)) {
|
99
|
+
self_data->pj = pj_init_plus(RSTRING_PTR(orig_data->original_str));
|
100
|
+
}
|
101
|
+
else {
|
102
|
+
str = pj_get_def(orig_data->pj, 0);
|
103
|
+
self_data->pj = pj_init_plus(str);
|
104
|
+
pj_dalloc(str);
|
105
|
+
}
|
106
|
+
self_data->original_str = orig_data->original_str;
|
107
|
+
self_data->uses_radians = orig_data->uses_radians;
|
108
|
+
|
109
|
+
return self;
|
110
|
+
}
|
111
|
+
|
112
|
+
|
113
|
+
static VALUE method_proj4_set_value(VALUE self, VALUE str, VALUE uses_radians)
|
114
|
+
{
|
115
|
+
RGeo_Proj4Data* self_data;
|
116
|
+
projPJ pj;
|
117
|
+
|
118
|
+
Check_Type(str, T_STRING);
|
119
|
+
|
120
|
+
// 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
|
+
}
|
128
|
+
|
129
|
+
// Set new data
|
130
|
+
self_data->pj = pj_init_plus(RSTRING_PTR(str));
|
131
|
+
self_data->original_str = str;
|
132
|
+
self_data->uses_radians = RTEST(uses_radians) ? 1 : 0;
|
133
|
+
|
134
|
+
return self;
|
135
|
+
}
|
136
|
+
|
137
|
+
|
138
|
+
static VALUE method_proj4_get_geographic(VALUE self)
|
139
|
+
{
|
140
|
+
VALUE result;
|
141
|
+
RGeo_Proj4Data* new_data;
|
142
|
+
RGeo_Proj4Data* self_data;
|
143
|
+
|
144
|
+
result = Qnil;
|
145
|
+
new_data = ALLOC(RGeo_Proj4Data);
|
146
|
+
if (new_data) {
|
147
|
+
self_data = RGEO_PROJ4_DATA_PTR(self);
|
148
|
+
new_data->pj = pj_latlong_from_proj(self_data->pj);
|
149
|
+
new_data->original_str = Qnil;
|
150
|
+
new_data->uses_radians = self_data->uses_radians;
|
151
|
+
result = Data_Wrap_Struct(CLASS_OF(self), mark_proj4_func, destroy_proj4_func, new_data);
|
152
|
+
}
|
153
|
+
return result;
|
154
|
+
}
|
155
|
+
|
156
|
+
|
157
|
+
static VALUE method_proj4_original_str(VALUE self)
|
158
|
+
{
|
159
|
+
return RGEO_PROJ4_DATA_PTR(self)->original_str;
|
160
|
+
}
|
161
|
+
|
162
|
+
|
163
|
+
static VALUE method_proj4_uses_radians(VALUE self)
|
164
|
+
{
|
165
|
+
return RGEO_PROJ4_DATA_PTR(self)->uses_radians ? Qtrue : Qfalse;
|
166
|
+
}
|
167
|
+
|
168
|
+
|
169
|
+
static VALUE method_proj4_canonical_str(VALUE self)
|
170
|
+
{
|
171
|
+
VALUE result;
|
172
|
+
projPJ pj;
|
173
|
+
char* str;
|
174
|
+
|
175
|
+
result = Qnil;
|
176
|
+
pj = RGEO_PROJ4_DATA_PTR(self)->pj;
|
177
|
+
if (pj) {
|
178
|
+
str = pj_get_def(pj, 0);
|
179
|
+
if (str) {
|
180
|
+
result = rb_str_new2(str);
|
181
|
+
pj_dalloc(str);
|
182
|
+
}
|
183
|
+
}
|
184
|
+
return result;
|
185
|
+
}
|
186
|
+
|
187
|
+
|
188
|
+
static VALUE method_proj4_is_geographic(VALUE self)
|
189
|
+
{
|
190
|
+
VALUE result;
|
191
|
+
projPJ pj;
|
192
|
+
|
193
|
+
result = Qnil;
|
194
|
+
pj = RGEO_PROJ4_DATA_PTR(self)->pj;
|
195
|
+
if (pj) {
|
196
|
+
result = pj_is_latlong(pj) ? Qtrue : Qfalse;
|
197
|
+
}
|
198
|
+
return result;
|
199
|
+
}
|
200
|
+
|
201
|
+
|
202
|
+
static VALUE method_proj4_is_geocentric(VALUE self)
|
203
|
+
{
|
204
|
+
VALUE result;
|
205
|
+
projPJ pj;
|
206
|
+
|
207
|
+
result = Qnil;
|
208
|
+
pj = RGEO_PROJ4_DATA_PTR(self)->pj;
|
209
|
+
if (pj) {
|
210
|
+
result = pj_is_geocent(pj) ? Qtrue : Qfalse;
|
211
|
+
}
|
212
|
+
return result;
|
213
|
+
}
|
214
|
+
|
215
|
+
|
216
|
+
static VALUE method_proj4_is_valid(VALUE self)
|
217
|
+
{
|
218
|
+
return RGEO_PROJ4_DATA_PTR(self)->pj ? Qtrue : Qfalse;
|
219
|
+
}
|
220
|
+
|
221
|
+
|
222
|
+
static VALUE cmethod_proj4_version(VALUE module)
|
223
|
+
{
|
224
|
+
return INT2NUM(PJ_VERSION);
|
225
|
+
}
|
226
|
+
|
227
|
+
|
228
|
+
static VALUE cmethod_proj4_transform(VALUE module, VALUE from, VALUE to, VALUE x, VALUE y, VALUE z)
|
229
|
+
{
|
230
|
+
VALUE result;
|
231
|
+
projPJ from_pj;
|
232
|
+
projPJ to_pj;
|
233
|
+
double xval, yval, zval;
|
234
|
+
int err;
|
235
|
+
|
236
|
+
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
|
+
}
|
255
|
+
}
|
256
|
+
return result;
|
257
|
+
}
|
258
|
+
|
259
|
+
|
260
|
+
static VALUE cmethod_proj4_create(VALUE klass, VALUE str, VALUE uses_radians)
|
261
|
+
{
|
262
|
+
VALUE result;
|
263
|
+
RGeo_Proj4Data* data;
|
264
|
+
|
265
|
+
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);
|
273
|
+
}
|
274
|
+
return result;
|
275
|
+
}
|
276
|
+
|
277
|
+
|
278
|
+
static void rgeo_init_proj4()
|
279
|
+
{
|
280
|
+
VALUE rgeo_module;
|
281
|
+
VALUE coordsys_module;
|
282
|
+
VALUE proj4_class;
|
283
|
+
|
284
|
+
rgeo_module = rb_define_module("RGeo");
|
285
|
+
coordsys_module = rb_define_module_under(rgeo_module, "CoordSys");
|
286
|
+
proj4_class = rb_define_class_under(coordsys_module, "Proj4", rb_cObject);
|
287
|
+
|
288
|
+
rb_define_alloc_func(proj4_class, alloc_proj4);
|
289
|
+
rb_define_module_function(proj4_class, "_create", cmethod_proj4_create, 2);
|
290
|
+
rb_define_method(proj4_class, "initialize_copy", method_proj4_initialize_copy, 1);
|
291
|
+
rb_define_method(proj4_class, "_set_value", method_proj4_set_value, 2);
|
292
|
+
rb_define_method(proj4_class, "_original_str", method_proj4_original_str, 0);
|
293
|
+
rb_define_method(proj4_class, "_canonical_str", method_proj4_canonical_str, 0);
|
294
|
+
rb_define_method(proj4_class, "_valid?", method_proj4_is_valid, 0);
|
295
|
+
rb_define_method(proj4_class, "_geographic?", method_proj4_is_geographic, 0);
|
296
|
+
rb_define_method(proj4_class, "_geocentric?", method_proj4_is_geocentric, 0);
|
297
|
+
rb_define_method(proj4_class, "_radians?", method_proj4_uses_radians, 0);
|
298
|
+
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
|
+
rb_define_module_function(proj4_class, "_proj_version", cmethod_proj4_version, 0);
|
301
|
+
}
|
302
|
+
|
303
|
+
|
304
|
+
#endif
|
305
|
+
|
306
|
+
|
307
|
+
void Init_proj4_c_impl()
|
308
|
+
{
|
309
|
+
#ifdef RGEO_PROJ4_SUPPORTED
|
310
|
+
rgeo_init_proj4();
|
311
|
+
#endif
|
312
|
+
}
|
313
|
+
|
314
|
+
|
315
|
+
RGEO_END_C
|
@@ -0,0 +1,293 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Proj4 wrapper for RGeo
|
4
|
+
#
|
5
|
+
# -----------------------------------------------------------------------------
|
6
|
+
|
7
|
+
module RGeo
|
8
|
+
module CoordSys
|
9
|
+
# This is a Ruby wrapper around a Proj4 coordinate system.
|
10
|
+
# It represents a single geographic coordinate system, which may be
|
11
|
+
# a flat projection, a geocentric (3-dimensional) coordinate system,
|
12
|
+
# or a geographic (latitude-longitude) coordinate system.
|
13
|
+
#
|
14
|
+
# Generally, these are used to define the projection for a
|
15
|
+
# Feature::Factory. You can then convert between coordinate systems
|
16
|
+
# by casting geometries between such factories using the :project
|
17
|
+
# option. You may also use this object directly to perform low-level
|
18
|
+
# coordinate transformations.
|
19
|
+
|
20
|
+
class Proj4
|
21
|
+
def inspect # :nodoc:
|
22
|
+
"#<#{self.class}:0x#{object_id.to_s(16)} #{canonical_str.inspect}>"
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s # :nodoc:
|
26
|
+
canonical_str
|
27
|
+
end
|
28
|
+
|
29
|
+
def hash # :nodoc:
|
30
|
+
@hash ||= canonical_hash.hash
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns true if this Proj4 is equivalent to the given Proj4.
|
34
|
+
#
|
35
|
+
# Note: this tests for equivalence by comparing only the hash
|
36
|
+
# definitions of the Proj4 objects, and returning true if those
|
37
|
+
# definitions are equivalent. In some cases, this may still return
|
38
|
+
# false even if the actual coordinate systems are identical, since
|
39
|
+
# there are sometimes multiple ways to express a given coordinate
|
40
|
+
# system.
|
41
|
+
|
42
|
+
def eql?(rhs_)
|
43
|
+
rhs_.class == self.class && rhs_.canonical_hash == canonical_hash && rhs_._radians? == _radians?
|
44
|
+
end
|
45
|
+
alias_method :==, :eql?
|
46
|
+
|
47
|
+
# Marshal support
|
48
|
+
|
49
|
+
def marshal_dump # :nodoc:
|
50
|
+
{ "rad" => radians?, "str" => original_str || canonical_str }
|
51
|
+
end
|
52
|
+
|
53
|
+
def marshal_load(data_) # :nodoc:
|
54
|
+
_set_value(data_["str"], data_["rad"])
|
55
|
+
end
|
56
|
+
|
57
|
+
# Psych support
|
58
|
+
|
59
|
+
def encode_with(coder_) # :nodoc:
|
60
|
+
coder_["proj4"] = original_str || canonical_str
|
61
|
+
coder_["radians"] = radians?
|
62
|
+
end
|
63
|
+
|
64
|
+
def init_with(coder_) # :nodoc:
|
65
|
+
if coder_.type == :scalar
|
66
|
+
_set_value(coder_.scalar, false)
|
67
|
+
else
|
68
|
+
_set_value(coder_["proj4"], coder_["radians"])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns the "canonical" string definition for this coordinate
|
73
|
+
# system, as reported by Proj4. This may be slightly different
|
74
|
+
# from the definition used to construct this object.
|
75
|
+
|
76
|
+
def canonical_str
|
77
|
+
unless defined?(@canonical_str)
|
78
|
+
@canonical_str = _canonical_str
|
79
|
+
if @canonical_str.respond_to?(:force_encoding)
|
80
|
+
@canonical_str.force_encoding("US-ASCII")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
@canonical_str
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns the "canonical" hash definition for this coordinate
|
87
|
+
# system, as reported by Proj4. This may be slightly different
|
88
|
+
# from the definition used to construct this object.
|
89
|
+
|
90
|
+
def canonical_hash
|
91
|
+
unless defined?(@canonical_hash)
|
92
|
+
@canonical_hash = {}
|
93
|
+
canonical_str.strip.split(/\s+/).each do |elem_|
|
94
|
+
@canonical_hash[Regexp.last_match(1)] = Regexp.last_match(3) if elem_ =~ /^\+(\w+)(=(\S+))?$/
|
95
|
+
end
|
96
|
+
end
|
97
|
+
@canonical_hash
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns the string definition originally used to construct this
|
101
|
+
# object. Returns nil if this object wasn't created by a string
|
102
|
+
# definition; i.e. if it was created using get_geographic.
|
103
|
+
|
104
|
+
def original_str
|
105
|
+
_original_str
|
106
|
+
end
|
107
|
+
|
108
|
+
# Returns true if this Proj4 object is a geographic (lat-long)
|
109
|
+
# coordinate system.
|
110
|
+
|
111
|
+
def geographic?
|
112
|
+
_geographic?
|
113
|
+
end
|
114
|
+
|
115
|
+
# Returns true if this Proj4 object is a geocentric (3dz)
|
116
|
+
# coordinate system.
|
117
|
+
|
118
|
+
def geocentric?
|
119
|
+
_geocentric?
|
120
|
+
end
|
121
|
+
|
122
|
+
# Returns true if this Proj4 object uses radians rather than degrees
|
123
|
+
# if it is a geographic coordinate system.
|
124
|
+
|
125
|
+
def radians?
|
126
|
+
_radians?
|
127
|
+
end
|
128
|
+
|
129
|
+
# Get the geographic (unprojected lat-long) coordinate system
|
130
|
+
# corresponding to this coordinate system; i.e. the one that uses
|
131
|
+
# the same ellipsoid and datum.
|
132
|
+
|
133
|
+
def get_geographic
|
134
|
+
_get_geographic
|
135
|
+
end
|
136
|
+
|
137
|
+
class << self
|
138
|
+
# Returns true if Proj4 is supported in this installation.
|
139
|
+
# If this returns false, the other methods such as create
|
140
|
+
# will not work.
|
141
|
+
|
142
|
+
def supported?
|
143
|
+
respond_to?(:_create)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Returns the Proj library version as an integer (example: 493).
|
147
|
+
# TODO: return as string of the format "x.y.z".
|
148
|
+
def version
|
149
|
+
_proj_version
|
150
|
+
end
|
151
|
+
|
152
|
+
# Create a new Proj4 object, given a definition, which may be
|
153
|
+
# either a string or a hash. Returns nil if the given definition
|
154
|
+
# is invalid or Proj4 is not supported.
|
155
|
+
#
|
156
|
+
# Recognized options include:
|
157
|
+
#
|
158
|
+
# [<tt>:radians</tt>]
|
159
|
+
# If set to true, then this proj4 will represent geographic
|
160
|
+
# (latitude/longitude) coordinates in radians rather than
|
161
|
+
# degrees. If this is a geographic coordinate system, then its
|
162
|
+
# units will be in radians. If this is a projected coordinate
|
163
|
+
# system, then its units will be unchanged, but any geographic
|
164
|
+
# coordinate system obtained using get_geographic will use
|
165
|
+
# radians as its units. If this is a geocentric or other type of
|
166
|
+
# coordinate system, this has no effect. Default is false.
|
167
|
+
# (That is all coordinates are in degrees by default.)
|
168
|
+
|
169
|
+
def create(defn_, opts_ = {})
|
170
|
+
result_ = nil
|
171
|
+
if supported?
|
172
|
+
if defn_.is_a?(::Hash)
|
173
|
+
defn_ = defn_.map { |k_, v_| v_ ? "+#{k_}=#{v_}" : "+#{k_}" }.join(" ")
|
174
|
+
end
|
175
|
+
unless defn_ =~ /^\s*\+/
|
176
|
+
defn_ = defn_.sub(/^(\s*)/, '\1+').gsub(/(\s+)([^+\s])/, '\1+\2')
|
177
|
+
end
|
178
|
+
result_ = _create(defn_, opts_[:radians])
|
179
|
+
result_ = nil unless result_._valid?
|
180
|
+
end
|
181
|
+
result_
|
182
|
+
end
|
183
|
+
|
184
|
+
# Create a new Proj4 object, given a definition, which may be
|
185
|
+
# either a string or a hash. Raises Error::UnsupportedOperation
|
186
|
+
# if the given definition is invalid or Proj4 is not supported.
|
187
|
+
#
|
188
|
+
# Recognized options include:
|
189
|
+
#
|
190
|
+
# [<tt>:radians</tt>]
|
191
|
+
# If set to true, then this proj4 will represent geographic
|
192
|
+
# (latitude/longitude) coordinates in radians rather than
|
193
|
+
# degrees. If this is a geographic coordinate system, then its
|
194
|
+
# units will be in radians. If this is a projected coordinate
|
195
|
+
# system, then its units will be unchanged, but any geographic
|
196
|
+
# coordinate system obtained using get_geographic will use
|
197
|
+
# radians as its units. If this is a geocentric or other type of
|
198
|
+
# coordinate system, this has no effect. Default is false.
|
199
|
+
# (That is all coordinates are in degrees by default.)
|
200
|
+
|
201
|
+
def new(defn_, opts_ = {})
|
202
|
+
result_ = create(defn_, opts_)
|
203
|
+
unless result_
|
204
|
+
raise Error::UnsupportedOperation, "Proj4 not supported in this installation"
|
205
|
+
end
|
206
|
+
result_
|
207
|
+
end
|
208
|
+
|
209
|
+
# Low-level coordinate transform method.
|
210
|
+
# Transforms the given coordinate (x, y, [z]) from one proj4
|
211
|
+
# coordinate system to another. Returns an array with either two
|
212
|
+
# or three elements.
|
213
|
+
|
214
|
+
def transform_coords(from_proj_, to_proj_, x_, y_, z_ = nil)
|
215
|
+
if !from_proj_._radians? && from_proj_._geographic?
|
216
|
+
x_ *= ImplHelper::Math::RADIANS_PER_DEGREE
|
217
|
+
y_ *= ImplHelper::Math::RADIANS_PER_DEGREE
|
218
|
+
end
|
219
|
+
result_ = _transform_coords(from_proj_, to_proj_, x_, y_, z_)
|
220
|
+
if result_ && !to_proj_._radians? && to_proj_._geographic?
|
221
|
+
result_[0] *= ImplHelper::Math::DEGREES_PER_RADIAN
|
222
|
+
result_[1] *= ImplHelper::Math::DEGREES_PER_RADIAN
|
223
|
+
end
|
224
|
+
result_
|
225
|
+
end
|
226
|
+
|
227
|
+
# Low-level geometry transform method.
|
228
|
+
# Transforms the given geometry between the given two projections.
|
229
|
+
# The resulting geometry is constructed using the to_factory.
|
230
|
+
# Any projections associated with the factories themselves are
|
231
|
+
# ignored.
|
232
|
+
|
233
|
+
def transform(from_proj_, from_geometry_, to_proj_, to_factory_)
|
234
|
+
case from_geometry_
|
235
|
+
when Feature::Point
|
236
|
+
_transform_point(from_proj_, from_geometry_, to_proj_, to_factory_)
|
237
|
+
when Feature::Line
|
238
|
+
to_factory_.line(from_geometry_.points.map { |p_| _transform_point(from_proj_, p_, to_proj_, to_factory_) })
|
239
|
+
when Feature::LinearRing
|
240
|
+
_transform_linear_ring(from_proj_, from_geometry_, to_proj_, to_factory_)
|
241
|
+
when Feature::LineString
|
242
|
+
to_factory_.line_string(from_geometry_.points.map { |p_| _transform_point(from_proj_, p_, to_proj_, to_factory_) })
|
243
|
+
when Feature::Polygon
|
244
|
+
_transform_polygon(from_proj_, from_geometry_, to_proj_, to_factory_)
|
245
|
+
when Feature::MultiPoint
|
246
|
+
to_factory_.multi_point(from_geometry_.map { |p_| _transform_point(from_proj_, p_, to_proj_, to_factory_) })
|
247
|
+
when Feature::MultiLineString
|
248
|
+
to_factory_.multi_line_string(from_geometry_.map { |g_| transform(from_proj_, g_, to_proj_, to_factory_) })
|
249
|
+
when Feature::MultiPolygon
|
250
|
+
to_factory_.multi_polygon(from_geometry_.map { |p_| _transform_polygon(from_proj_, p_, to_proj_, to_factory_) })
|
251
|
+
when Feature::GeometryCollection
|
252
|
+
to_factory_.collection(from_geometry_.map { |g_| transform(from_proj_, g_, to_proj_, to_factory_) })
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def _transform_point(from_proj_, from_point_, to_proj_, to_factory_) # :nodoc:
|
257
|
+
from_factory_ = from_point_.factory
|
258
|
+
from_has_z_ = from_factory_.property(:has_z_coordinate)
|
259
|
+
from_has_m_ = from_factory_.property(:has_m_coordinate)
|
260
|
+
to_has_z_ = to_factory_.property(:has_z_coordinate)
|
261
|
+
to_has_m_ = to_factory_.property(:has_m_coordinate)
|
262
|
+
x_ = from_point_.x
|
263
|
+
y_ = from_point_.y
|
264
|
+
if !from_proj_._radians? && from_proj_._geographic?
|
265
|
+
x_ *= ImplHelper::Math::RADIANS_PER_DEGREE
|
266
|
+
y_ *= ImplHelper::Math::RADIANS_PER_DEGREE
|
267
|
+
end
|
268
|
+
coords_ = _transform_coords(from_proj_, to_proj_, x_, y_, from_has_z_ ? from_point_.z : nil)
|
269
|
+
if coords_
|
270
|
+
if !to_proj_._radians? && to_proj_._geographic?
|
271
|
+
coords_[0] *= ImplHelper::Math::DEGREES_PER_RADIAN
|
272
|
+
coords_[1] *= ImplHelper::Math::DEGREES_PER_RADIAN
|
273
|
+
end
|
274
|
+
extras_ = []
|
275
|
+
extras_ << coords_[2].to_f if to_has_z_
|
276
|
+
extras_ << from_has_m_ ? from_point_.m : 0.0 if to_has_m_
|
277
|
+
to_factory_.point(coords_[0], coords_[1], *extras_)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
def _transform_linear_ring(from_proj_, from_ring_, to_proj_, to_factory_) # :nodoc:
|
282
|
+
to_factory_.linear_ring(from_ring_.points[0..-2].map { |p_| _transform_point(from_proj_, p_, to_proj_, to_factory_) })
|
283
|
+
end
|
284
|
+
|
285
|
+
def _transform_polygon(from_proj_, from_polygon_, to_proj_, to_factory_) # :nodoc:
|
286
|
+
ext_ = _transform_linear_ring(from_proj_, from_polygon_.exterior_ring, to_proj_, to_factory_)
|
287
|
+
int_ = from_polygon_.interior_rings.map { |r_| _transform_linear_ring(from_proj_, r_, to_proj_, to_factory_) }
|
288
|
+
to_factory_.polygon(ext_, int_)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# SRS database interface
|
4
|
+
#
|
5
|
+
# -----------------------------------------------------------------------------
|
6
|
+
|
7
|
+
module RGeo
|
8
|
+
module CoordSys
|
9
|
+
module SRSDatabase
|
10
|
+
# A spatial reference database implementation backed by coordinate
|
11
|
+
# system files installed as part of the proj4 library. For a given
|
12
|
+
# Proj4Data object, you specify a single file (e.g. the epsg data
|
13
|
+
# file), and you can retrieve records by ID number.
|
14
|
+
|
15
|
+
class Proj4Data
|
16
|
+
# Connect to one of the proj4 data files. You should provide the
|
17
|
+
# file name, optionally the installation directory if it is not
|
18
|
+
# in a typical location, and several additional options.
|
19
|
+
#
|
20
|
+
# These options are recognized:
|
21
|
+
#
|
22
|
+
# [<tt>:dir</tt>]
|
23
|
+
# The path for the share/proj directory that contains the
|
24
|
+
# requested data file. By default, the Proj4Data class will
|
25
|
+
# try a number of directories for you, including
|
26
|
+
# /usr/local/share/proj, /opt/local/share/proj, /usr/share/proj,
|
27
|
+
# and a few other variants. However, if you have proj4 installed
|
28
|
+
# elsewhere, you can provide an explicit directory using this
|
29
|
+
# option. You may also pass nil as the value, in which case all
|
30
|
+
# the normal lookup paths will be disabled, and you will have to
|
31
|
+
# provide the full path as the file name.
|
32
|
+
# [<tt>:cache</tt>]
|
33
|
+
# If set to true, this class caches previously looked up entries
|
34
|
+
# so subsequent lookups do not have to reread the file. If set
|
35
|
+
# to <tt>:read_all</tt>, then ALL values in the file are read in
|
36
|
+
# and cached the first time a lookup is done. If set to
|
37
|
+
# <tt>:preload</tt>, then ALL values in the file are read in
|
38
|
+
# immediately when the database is created. Default is false,
|
39
|
+
# indicating that the file will be reread on every lookup.
|
40
|
+
# [<tt>:authority</tt>]
|
41
|
+
# If set, its value is taken as the authority name for all
|
42
|
+
# entries. The authority code will be set to the identifier. If
|
43
|
+
# not set, then the authority fields of entries will be blank.
|
44
|
+
|
45
|
+
def initialize(filename_, opts_ = {})
|
46
|
+
dir_ = nil
|
47
|
+
if opts_.include?(:dir)
|
48
|
+
dir_ = opts_[:dir]
|
49
|
+
else
|
50
|
+
["/usr/local/share/proj", "/usr/local/proj/share/proj", "/usr/local/proj4/share/proj", "/opt/local/share/proj", "/opt/proj/share/proj", "/opt/proj4/share/proj", "/opt/share/proj", "/usr/share/proj"].each do |d_|
|
51
|
+
if ::File.directory?(d_) && ::File.readable?(d_)
|
52
|
+
dir_ = d_
|
53
|
+
break
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
@path = dir_ ? "#{dir_}/#{filename_}" : filename_
|
58
|
+
@authority = opts_[:authority]
|
59
|
+
if opts_[:cache]
|
60
|
+
@cache = {}
|
61
|
+
case opts_[:cache]
|
62
|
+
when :read_all
|
63
|
+
@populate_state = 1
|
64
|
+
when :preload
|
65
|
+
_search_file(nil)
|
66
|
+
@populate_state = 2
|
67
|
+
else
|
68
|
+
@populate_state = 0
|
69
|
+
end
|
70
|
+
else
|
71
|
+
@cache = nil
|
72
|
+
@populate_state = 0
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Retrieve the Entry for the given ID number.
|
77
|
+
|
78
|
+
def get(ident_)
|
79
|
+
ident_ = ident_.to_s
|
80
|
+
return @cache[ident_] if @cache && @cache.include?(ident_)
|
81
|
+
result_ = nil
|
82
|
+
if @populate_state == 0
|
83
|
+
data_ = _search_file(ident_)
|
84
|
+
result_ = Entry.new(ident_, authority: @authority, authority_code: @authority ? ident_ : nil, name: data_[1], proj4: data_[2]) if data_
|
85
|
+
@cache[ident_] = result_ if @cache
|
86
|
+
elsif @populate_state == 1
|
87
|
+
_search_file(nil)
|
88
|
+
result_ = @cache[ident_]
|
89
|
+
@populate_state = 2
|
90
|
+
end
|
91
|
+
result_
|
92
|
+
end
|
93
|
+
|
94
|
+
# Clear the cache if one exists.
|
95
|
+
|
96
|
+
def clear_cache
|
97
|
+
@cache.clear if @cache
|
98
|
+
@populate_state = 1 if @populate_state == 2
|
99
|
+
end
|
100
|
+
|
101
|
+
def _search_file(ident_) # :nodoc:
|
102
|
+
::File.open(@path) do |file_|
|
103
|
+
cur_name_ = nil
|
104
|
+
cur_ident_ = nil
|
105
|
+
cur_text_ = nil
|
106
|
+
file_.each do |line_|
|
107
|
+
line_.strip!
|
108
|
+
if (comment_delim_ = line_.index('#'))
|
109
|
+
cur_name_ = line_[comment_delim_ + 1..-1].strip
|
110
|
+
line_ = line_[0..comment_delim_ - 1].strip
|
111
|
+
end
|
112
|
+
unless cur_ident_
|
113
|
+
if line_ =~ /^<(\w+)>(.*)/
|
114
|
+
cur_ident_ = Regexp.last_match(1)
|
115
|
+
cur_text_ = []
|
116
|
+
line_ = Regexp.last_match(2).strip
|
117
|
+
end
|
118
|
+
end
|
119
|
+
next unless cur_ident_
|
120
|
+
if line_[-2..-1] == "<>"
|
121
|
+
cur_text_ << line_[0..-3].strip
|
122
|
+
cur_text_ = cur_text_.join(" ")
|
123
|
+
if ident_.nil?
|
124
|
+
@cache[ident_] = Entry.new(ident_, authority: @authority, authority_code: @authority ? id_ : nil, name: cur_name_, proj4: cur_text_)
|
125
|
+
end
|
126
|
+
return [ident_, cur_name_, cur_text_] if cur_ident_ == ident_
|
127
|
+
cur_ident_ = nil
|
128
|
+
cur_name_ = nil
|
129
|
+
cur_text_ = nil
|
130
|
+
else
|
131
|
+
cur_text_ << line_
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
nil
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
data/lib/rgeo/proj4.rb
ADDED
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rgeo-proj4
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0.rc1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tee Parham, Daniel Azuma
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-11-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rgeo
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.0.0.rc1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.0.0.rc1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.16'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.16'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '12.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '12.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake-compiler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: test-unit
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
description: Proj4 extension for rgeo.
|
84
|
+
email:
|
85
|
+
- parhameter@gmail.com, dazuma@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions:
|
88
|
+
- ext/proj4_c_impl/extconf.rb
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- LICENSE.txt
|
92
|
+
- ext/proj4_c_impl/extconf.rb
|
93
|
+
- ext/proj4_c_impl/main.c
|
94
|
+
- lib/rgeo/coord_sys/proj4.rb
|
95
|
+
- lib/rgeo/coord_sys/srs_database/proj4_data.rb
|
96
|
+
- lib/rgeo/proj4.rb
|
97
|
+
- lib/rgeo/proj4/version.rb
|
98
|
+
homepage: https://github.com/rgeo/rgeo-proj4
|
99
|
+
licenses:
|
100
|
+
- MIT
|
101
|
+
metadata: {}
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 2.1.0
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">"
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: 1.3.1
|
116
|
+
requirements: []
|
117
|
+
rubyforge_project:
|
118
|
+
rubygems_version: 2.7.0
|
119
|
+
signing_key:
|
120
|
+
specification_version: 4
|
121
|
+
summary: Proj4 extension for rgeo.
|
122
|
+
test_files: []
|