simple-proj 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/API.md +111 -0
- data/README.md +120 -0
- data/Rakefile +8 -0
- data/ext/extconf.rb +12 -0
- data/ext/rb_proj.c +832 -0
- data/ext/rb_proj.h +18 -0
- data/lib/simple-proj.rb +165 -0
- data/simple-proj.gemspec +25 -0
- metadata +52 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2e3c3bb3834d7d494e1e0dc220c5ca30e6a9387e0cfea7a30223535ad44e6ec9
|
4
|
+
data.tar.gz: 96148f842f3f3334424705f1256acbd46699cbe3a11f4c492033f371967a7b70
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9359cd9bf7da8619a17d04ad615e713e70c89ccf56bf44f1f967b9324343481294de4dc5ba707a277f380784862cffce79dcb91d2bc466b75b0d938fb14fb4bb
|
7
|
+
data.tar.gz: e384f056f0a3a919dbc5e2d63d441a2c1222eff579583a442bbd327c974de1afcad4f4c2a88899f8b101268fb318b72edef5d32b99b49a3f65aa93b51c8550d4
|
data/API.md
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
PROJ4R
|
2
|
+
=======
|
3
|
+
|
4
|
+
Notes for the forward or inverse transformations
|
5
|
+
|
6
|
+
Proj
|
7
|
+
forward : (lon, lat) -> (x, y)
|
8
|
+
inverse : (x, y) -> (lon, lat)
|
9
|
+
|
10
|
+
Proj.transform
|
11
|
+
forward : (src, dst, x, y, z) -> (x', y', z')
|
12
|
+
inverse : (dst, src, x, y, z) -> (x', y', z')
|
13
|
+
|
14
|
+
If src = latlong, dst = projection, they work like Proj#forward, Proj#inverse.
|
15
|
+
In the cases, use lon as x, lat as y.
|
16
|
+
|
17
|
+
Geod
|
18
|
+
forward : (lat1, lon1, az12, dist) -> (lat2, lon2, az21)
|
19
|
+
inverse : (lat1, lon1, lat2, lon2) -> (dist, az12, az21)
|
20
|
+
|
21
|
+
Notes for the order of geographic coordinate (lon,lat) or (lat,lon)
|
22
|
+
|
23
|
+
PROJ4::Proj -> (lon, lat)
|
24
|
+
PROJ4.transform -> (lon, lat)
|
25
|
+
PROJ4::Geod -> (lat, lon)
|
26
|
+
|
27
|
+
These differences originate from Proj.4 C API.
|
28
|
+
|
29
|
+
Proj
|
30
|
+
----
|
31
|
+
|
32
|
+
### Constructor
|
33
|
+
|
34
|
+
pj = PROJ4::Proj.new("+proj=latlon +ellps=WGS84 units=km")
|
35
|
+
|
36
|
+
### Attributes
|
37
|
+
|
38
|
+
pj.definition
|
39
|
+
pj.latlong?
|
40
|
+
pj.geocent?
|
41
|
+
|
42
|
+
### Replication
|
43
|
+
|
44
|
+
pj.to_latlong
|
45
|
+
|
46
|
+
### forward
|
47
|
+
|
48
|
+
x, y = pj.forward(lon, lat)
|
49
|
+
|
50
|
+
### inverse
|
51
|
+
|
52
|
+
lon, lat = *pj.inverse(x, y)
|
53
|
+
|
54
|
+
p pj.to_latlong.definition
|
55
|
+
|
56
|
+
Transformation
|
57
|
+
--------------
|
58
|
+
|
59
|
+
|
60
|
+
Geod
|
61
|
+
----
|
62
|
+
|
63
|
+
### Constructor
|
64
|
+
|
65
|
+
geod = PROJ4::Geod.new() ### [+ellps=WGS84, units=m]
|
66
|
+
geod = PROJ4::Geod.new(6378137.0, 1/298.257223563) ### [+ellps=WGS84, units=m]
|
67
|
+
geod = PROJ4::Geod.new("+ellps=WGS84 units=m")
|
68
|
+
|
69
|
+
### forward
|
70
|
+
|
71
|
+
lat2, lon2, az21 = *geod.forward(lat1, lon1, az12, dist)
|
72
|
+
|
73
|
+
input:
|
74
|
+
lat1: latitude of original point in degree
|
75
|
+
lon1: longitudde of original point in degree
|
76
|
+
az12: azimuth point to target point at origninal point in degree
|
77
|
+
dist: distance from original point to target point in units defined in constructor
|
78
|
+
|
79
|
+
output:
|
80
|
+
lat2: latitude of target point in degree
|
81
|
+
lon1: longitude of target point in degree
|
82
|
+
az21: azimuth point to original point at target point in degree
|
83
|
+
|
84
|
+
### inverse
|
85
|
+
|
86
|
+
dist, az12, az21 = *geod.inverse(lat1, lon1, lat2, lon2)
|
87
|
+
|
88
|
+
input:
|
89
|
+
lat1: latitude of original point in degree
|
90
|
+
lon1: longitude of original point in degree
|
91
|
+
lat2: latitude of target point in degree
|
92
|
+
lon2: longitude of target point in degree
|
93
|
+
|
94
|
+
output:
|
95
|
+
dist: distance between original and target points in units defined in constructor
|
96
|
+
az12: azimuth point to target point at origninal point in degree
|
97
|
+
az21: azimuth point to original point at target point in degree
|
98
|
+
|
99
|
+
### distance
|
100
|
+
|
101
|
+
dist = *geod.distance(lat1, lon1, lat2, lon2)
|
102
|
+
|
103
|
+
input:
|
104
|
+
lat1: latitude of original point in degree
|
105
|
+
lon1: longitude of original point in degree
|
106
|
+
lat2: latitude of target point in degree
|
107
|
+
lon2: longitude of target point in degree
|
108
|
+
|
109
|
+
output:
|
110
|
+
dist: distance between original and target points in units defined in constructor
|
111
|
+
|
data/README.md
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
Simple PROJ
|
2
|
+
===========
|
3
|
+
|
4
|
+
This is a ruby extension library for map projection conversion using PROJ.
|
5
|
+
Note that this library is not a verbatim wrapper for all functions in PROJ.
|
6
|
+
|
7
|
+
Installation
|
8
|
+
------------
|
9
|
+
|
10
|
+
gem install simple-proj
|
11
|
+
|
12
|
+
### Requirement
|
13
|
+
|
14
|
+
* PROJ version 6 or 7
|
15
|
+
|
16
|
+
Features
|
17
|
+
--------
|
18
|
+
|
19
|
+
* Conversion of latitude and longitude to map projection coordinates (forward transformation)
|
20
|
+
* Conversion of map projection coordinates to latitude and longitude (inverse transformation)
|
21
|
+
* Transformation a map projection coordinate to another map projection coordinate
|
22
|
+
|
23
|
+
|
24
|
+
Usage
|
25
|
+
-----
|
26
|
+
|
27
|
+
### Constructor
|
28
|
+
|
29
|
+
PROJ.new(PROJ_STRING) ### transform defined by proj-string
|
30
|
+
PROJ.new(CRS1, CRS2) ### transform from CRS1 to CRS2
|
31
|
+
PROJ.new(CRS2) ### CRS1 is assumed to be "+proj=latlong +type=crs"
|
32
|
+
|
33
|
+
The arguments should be String objects one of
|
34
|
+
|
35
|
+
* a proj-string,
|
36
|
+
* a WKT string,
|
37
|
+
* an object code (like 'EPSG:4326', 'urn:ogc:def:crs:EPSG::4326',
|
38
|
+
'urn:ogc:def:coordinateOperation:EPSG::1671'),
|
39
|
+
* a OGC URN combining references for compound coordinate reference
|
40
|
+
systems (e.g 'urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717' or
|
41
|
+
custom abbreviated syntax 'EPSG:2393+5717'),
|
42
|
+
* a OGC URN combining references for concatenated operations (e.g.
|
43
|
+
'urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,
|
44
|
+
coordinateOperation:EPSG::1618')
|
45
|
+
|
46
|
+
### Transformation
|
47
|
+
|
48
|
+
Forward transformation.
|
49
|
+
|
50
|
+
PROJ#transform(x1, y1, z1=nil) => x2, y2[, z2]
|
51
|
+
PROJ#transform_forward(x1, y1, z1=nil) => x2, y2[, z2] ### alias of #transform
|
52
|
+
|
53
|
+
Inverse transformation.
|
54
|
+
|
55
|
+
PROJ#transform_inverse(x1, y1, z1=nil) => x2, y2[, z2]
|
56
|
+
|
57
|
+
|
58
|
+
### Special methods for transformation from geodetic coordinates and other coordinates.
|
59
|
+
|
60
|
+
These are special methods provided to avoid converting
|
61
|
+
between latitude and longitude units between degrees and radians.
|
62
|
+
|
63
|
+
PROJ#forward(lon1, lat1, z1=nil) => x2, y2[, z2]
|
64
|
+
PROJ#inverse(x1, y1, z1=nil) => lon2, lat2[, z2]
|
65
|
+
|
66
|
+
The units of input parameters for PROJ#forward are degrees.
|
67
|
+
Internally, these units are converted to radians and passed to proj_trans().
|
68
|
+
Similarly, the units of output parameters for PROJ#inverse are degrees.
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
proj = PROJ.new("+proj=webmerc")
|
72
|
+
|
73
|
+
x, y = proj.forward(135, 35)
|
74
|
+
p [x, y] ### => [15028131.257091932, 4163881.144064294]
|
75
|
+
|
76
|
+
lon, lat = proj.inverse(x, y)
|
77
|
+
p [lon, lat] ### => [135.0, 35.000000000000014]
|
78
|
+
|
79
|
+
```
|
80
|
+
|
81
|
+
Note:
|
82
|
+
This method should not be used for the PROJ object initialized with the proj-string that do not expect geodetic coordinates in radians for the source CRS. In such case, use #transform_forward and #transform_inverse instead of #forward and #inverse.
|
83
|
+
This is an example for *invalid* usage.
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
pj = PROJ.new("+proj=unitconvert +xy_in=deg +xy_out=rad")
|
87
|
+
|
88
|
+
p pj.forward(135, 35) ### does not work as expected
|
89
|
+
# => [0.041123351671205656, 0.0106616096925348]
|
90
|
+
|
91
|
+
p pj.transform(135, 35) ### works as expected
|
92
|
+
# => [2.356194490192345, 0.6108652381980153]
|
93
|
+
```
|
94
|
+
|
95
|
+
Examples
|
96
|
+
--------
|
97
|
+
|
98
|
+
### Conversion between 'EPSG:4326' and 'EPSG:3857'
|
99
|
+
|
100
|
+
For EPSG:4326 (in PROJ), the geographic coordinates are in the order of latitude and longitude.
|
101
|
+
Then, the transformation from EPSG:4326 requires the arguments in the order of latitude and longitude.
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
#########################################
|
105
|
+
# EPSG:4326 <-> EPSG:3857
|
106
|
+
#########################################
|
107
|
+
|
108
|
+
proj = PROJ.new("EPSG:4326", "EPSG:3857")
|
109
|
+
|
110
|
+
x, y = proj.transform(35, 135)
|
111
|
+
|
112
|
+
p [x, y] ### => [15028131.257091932, 4163881.144064294]
|
113
|
+
|
114
|
+
lat, lon = proj.transform_inverse(x, y)
|
115
|
+
|
116
|
+
p [lat, lon] ### => [35.00000000000001, 135.0]
|
117
|
+
```
|
118
|
+
|
119
|
+
|
120
|
+
|
data/Rakefile
ADDED
data/ext/extconf.rb
ADDED
data/ext/rb_proj.c
ADDED
@@ -0,0 +1,832 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
#include "rb_proj.h"
|
3
|
+
|
4
|
+
VALUE rb_cProj;
|
5
|
+
VALUE rb_cCrs;
|
6
|
+
VALUE rb_mCommon;
|
7
|
+
|
8
|
+
ID id_forward;
|
9
|
+
ID id_inverse;
|
10
|
+
|
11
|
+
static VALUE
|
12
|
+
rb_proj_info (VALUE klass)
|
13
|
+
{
|
14
|
+
volatile VALUE vout;
|
15
|
+
PJ_INFO info;
|
16
|
+
|
17
|
+
info = proj_info();
|
18
|
+
|
19
|
+
vout = rb_hash_new();
|
20
|
+
rb_hash_aset(vout, rb_str_new2("major"), INT2NUM(info.major));
|
21
|
+
rb_hash_aset(vout, rb_str_new2("minor"), INT2NUM(info.minor));
|
22
|
+
rb_hash_aset(vout, rb_str_new2("patch"), INT2NUM(info.patch));
|
23
|
+
rb_hash_aset(vout, rb_str_new2("release"), rb_str_new2(info.release));
|
24
|
+
rb_hash_aset(vout, rb_str_new2("version"), rb_str_new2(info.version));
|
25
|
+
rb_hash_aset(vout, rb_str_new2("searchpath"), rb_str_new2(info.searchpath));
|
26
|
+
|
27
|
+
return vout;
|
28
|
+
}
|
29
|
+
|
30
|
+
static void
|
31
|
+
free_proj (Proj *proj)
|
32
|
+
{
|
33
|
+
if ( proj->ref ) {
|
34
|
+
proj_destroy(proj->ref);
|
35
|
+
}
|
36
|
+
free(proj);
|
37
|
+
}
|
38
|
+
|
39
|
+
static VALUE
|
40
|
+
rb_proj_s_allocate (VALUE klass)
|
41
|
+
{
|
42
|
+
Proj *proj;
|
43
|
+
return Data_Make_Struct(klass, Proj, 0, free_proj, proj);
|
44
|
+
}
|
45
|
+
|
46
|
+
/*
|
47
|
+
Constructs a transformation object with one or two arguments.
|
48
|
+
The arguments should be PROJ::CRS objects or String objects one of
|
49
|
+
|
50
|
+
* a proj-string,
|
51
|
+
* a WKT string,
|
52
|
+
* an object code (like “EPSG:4326”, “urn:ogc:def:crs:EPSG::4326”,
|
53
|
+
“urn:ogc:def:coordinateOperation:EPSG::1671”),
|
54
|
+
* a OGC URN combining references for compound coordinate reference
|
55
|
+
systems (e.g “urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717” or
|
56
|
+
custom abbreviated syntax “EPSG:2393+5717”),
|
57
|
+
* a OGC URN combining references for concatenated operations (e.g.
|
58
|
+
“urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,
|
59
|
+
coordinateOperation:EPSG::1618”)
|
60
|
+
|
61
|
+
If two arguments are given, the first is the source CRS definition and the
|
62
|
+
second is the target CRS definition. If only one argument is given, the
|
63
|
+
following two cases are possible.
|
64
|
+
|
65
|
+
* a proj-string, which represents a transformation.
|
66
|
+
* a CRS defintion, in which case the latlong coordinates are implicitly
|
67
|
+
used as the source CRS definition.
|
68
|
+
* a PROJ::CRS object
|
69
|
+
|
70
|
+
@overload initialize(def1, def2=nil)
|
71
|
+
@param def1 [String] proj-string or other CRS definition (see above description).
|
72
|
+
@param def2 [String, nil] proj-string or other CRS definition (see above description).
|
73
|
+
|
74
|
+
@example
|
75
|
+
# Transformation from EPSG:4326 to EPSG:3857
|
76
|
+
pj = PROJ.new("EPSG:4326", "EPSG:3857")
|
77
|
+
|
78
|
+
# Transformation from (lat,lon) to WEB Mercator.
|
79
|
+
pj = PROJ.new("+proj=webmerc")
|
80
|
+
|
81
|
+
# Transformation from (lat,lon) to EPSG:3857
|
82
|
+
pj = PROJ.new("EPSG:3857")
|
83
|
+
|
84
|
+
# Using PROJ::CRS objects
|
85
|
+
epsg_3857 = PROJ::CRS.new("EPSG:3857")
|
86
|
+
pj = PROJ.new(epsg_3857)
|
87
|
+
pj = PROJ.new("EPSG:4326", epsg_3857)
|
88
|
+
pj = PROJ.new(epsg_3857, "EPSG:4326")
|
89
|
+
|
90
|
+
*/
|
91
|
+
|
92
|
+
static VALUE
|
93
|
+
rb_proj_initialize (int argc, VALUE *argv, VALUE self)
|
94
|
+
{
|
95
|
+
volatile VALUE vdef1, vdef2;
|
96
|
+
Proj *proj, *crs;
|
97
|
+
PJ *ref, *src;
|
98
|
+
PJ_TYPE type;
|
99
|
+
int errno;
|
100
|
+
|
101
|
+
rb_scan_args(argc, argv, "11", (VALUE *)&vdef1, (VALUE *)&vdef2);
|
102
|
+
|
103
|
+
Data_Get_Struct(self, Proj, proj);
|
104
|
+
|
105
|
+
if ( NIL_P(vdef2) ) {
|
106
|
+
if ( rb_obj_is_kind_of(vdef1, rb_cCrs) ) {
|
107
|
+
Data_Get_Struct(vdef1, Proj, crs);
|
108
|
+
vdef1 = rb_str_new2(proj_as_proj_string(PJ_DEFAULT_CTX, crs->ref, PJ_PROJ_5, NULL));
|
109
|
+
}
|
110
|
+
else {
|
111
|
+
Check_Type(vdef1, T_STRING);
|
112
|
+
}
|
113
|
+
ref = proj_create(PJ_DEFAULT_CTX, StringValuePtr(vdef1));
|
114
|
+
if ( proj_is_crs(ref) ) {
|
115
|
+
proj_destroy(ref);
|
116
|
+
ref = proj_create_crs_to_crs(PJ_DEFAULT_CTX, "+proj=latlong +type=crs", StringValuePtr(vdef1), NULL);
|
117
|
+
proj->ref = ref;
|
118
|
+
proj->is_src_latlong = 2;
|
119
|
+
}
|
120
|
+
else {
|
121
|
+
proj->ref = ref;
|
122
|
+
proj->is_src_latlong = 1;
|
123
|
+
}
|
124
|
+
}
|
125
|
+
else {
|
126
|
+
if ( rb_obj_is_kind_of(vdef1, rb_cCrs) ) {
|
127
|
+
Data_Get_Struct(vdef1, Proj, crs);
|
128
|
+
vdef1 = rb_str_new2(proj_as_proj_string(PJ_DEFAULT_CTX, crs->ref, PJ_PROJ_5, NULL));
|
129
|
+
}
|
130
|
+
else {
|
131
|
+
Check_Type(vdef1, T_STRING);
|
132
|
+
}
|
133
|
+
if ( rb_obj_is_kind_of(vdef2, rb_cCrs) ) {
|
134
|
+
Data_Get_Struct(vdef2, Proj, crs);
|
135
|
+
vdef2 = rb_str_new2(proj_as_proj_string(PJ_DEFAULT_CTX, crs->ref, PJ_PROJ_5, NULL));
|
136
|
+
}
|
137
|
+
else {
|
138
|
+
Check_Type(vdef2, T_STRING);
|
139
|
+
}
|
140
|
+
ref = proj_create_crs_to_crs(PJ_DEFAULT_CTX, StringValuePtr(vdef1), StringValuePtr(vdef2), NULL);
|
141
|
+
proj->ref = ref;
|
142
|
+
src = proj_get_source_crs(PJ_DEFAULT_CTX, ref);
|
143
|
+
type = proj_get_type(src);
|
144
|
+
if ( type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
|
145
|
+
type == PJ_TYPE_GEOGRAPHIC_3D_CRS ) {
|
146
|
+
proj->is_src_latlong = 2;
|
147
|
+
}
|
148
|
+
else {
|
149
|
+
proj->is_src_latlong = 0;
|
150
|
+
}
|
151
|
+
}
|
152
|
+
|
153
|
+
if ( ! ref ) {
|
154
|
+
errno = proj_context_errno(PJ_DEFAULT_CTX);
|
155
|
+
rb_raise(rb_eRuntimeError, "%s", proj_errno_string(errno));
|
156
|
+
}
|
157
|
+
|
158
|
+
return Qnil;
|
159
|
+
}
|
160
|
+
|
161
|
+
/*
|
162
|
+
Normalizes the axis order which is the one expected for visualization purposes.
|
163
|
+
If the axis order of its source or target CRS is
|
164
|
+
northing, easting, then an axis swap operation will be inserted.
|
165
|
+
|
166
|
+
@return [self]
|
167
|
+
*/
|
168
|
+
static VALUE
|
169
|
+
rb_proj_normalize_for_visualization (VALUE self)
|
170
|
+
{
|
171
|
+
Proj *proj;
|
172
|
+
PJ *ref, *orig;
|
173
|
+
int errno;
|
174
|
+
|
175
|
+
Data_Get_Struct(self, Proj, proj);
|
176
|
+
orig = proj->ref;
|
177
|
+
|
178
|
+
ref = proj_normalize_for_visualization(PJ_DEFAULT_CTX, orig);
|
179
|
+
if ( ! ref ) {
|
180
|
+
errno = proj_context_errno(PJ_DEFAULT_CTX);
|
181
|
+
rb_raise(rb_eRuntimeError, "%s", proj_errno_string(errno));
|
182
|
+
}
|
183
|
+
|
184
|
+
proj->ref = ref;
|
185
|
+
|
186
|
+
proj_destroy(orig);
|
187
|
+
|
188
|
+
return self;
|
189
|
+
}
|
190
|
+
|
191
|
+
/*
|
192
|
+
Returns source CRS as PROJ::CRS object.
|
193
|
+
|
194
|
+
@return [PROJ, nil]
|
195
|
+
*/
|
196
|
+
static VALUE
|
197
|
+
rb_proj_source_crs (VALUE self)
|
198
|
+
{
|
199
|
+
Proj *proj;
|
200
|
+
PJ *crs;
|
201
|
+
|
202
|
+
Data_Get_Struct(self, Proj, proj);
|
203
|
+
crs = proj_get_source_crs(PJ_DEFAULT_CTX, proj->ref);
|
204
|
+
|
205
|
+
if ( ! crs ) {
|
206
|
+
return Qnil;
|
207
|
+
}
|
208
|
+
|
209
|
+
return rb_crs_new(crs);
|
210
|
+
}
|
211
|
+
|
212
|
+
/*
|
213
|
+
Returns target CRS as PROJ::CRS object.
|
214
|
+
|
215
|
+
@return [PROJ, nil]
|
216
|
+
*/
|
217
|
+
static VALUE
|
218
|
+
rb_proj_target_crs (VALUE self)
|
219
|
+
{
|
220
|
+
Proj *proj;
|
221
|
+
PJ *crs;
|
222
|
+
|
223
|
+
Data_Get_Struct(self, Proj, proj);
|
224
|
+
crs = proj_get_target_crs(PJ_DEFAULT_CTX, proj->ref);
|
225
|
+
|
226
|
+
if ( ! crs ) {
|
227
|
+
return Qnil;
|
228
|
+
}
|
229
|
+
|
230
|
+
return rb_crs_new(crs);
|
231
|
+
}
|
232
|
+
|
233
|
+
/*
|
234
|
+
Checks if a operation expects input in radians or not.
|
235
|
+
|
236
|
+
@return [Boolean]
|
237
|
+
*/
|
238
|
+
|
239
|
+
static VALUE
|
240
|
+
rb_proj_angular_input (VALUE self, VALUE direction)
|
241
|
+
{
|
242
|
+
Proj *proj;
|
243
|
+
|
244
|
+
Data_Get_Struct(self, Proj, proj);
|
245
|
+
|
246
|
+
if ( rb_to_id(direction) == id_forward ) {
|
247
|
+
return proj_angular_input(proj->ref, PJ_FWD) == 1 ? Qtrue : Qfalse;
|
248
|
+
}
|
249
|
+
else if ( rb_to_id(direction) == id_inverse ) {
|
250
|
+
return proj_angular_input(proj->ref, PJ_INV) == 1 ? Qtrue : Qfalse;
|
251
|
+
}
|
252
|
+
else {
|
253
|
+
rb_raise(rb_eArgError, "invalid direction");
|
254
|
+
}
|
255
|
+
}
|
256
|
+
|
257
|
+
/*
|
258
|
+
Checks if an operation returns output in radians or not.
|
259
|
+
|
260
|
+
@return [Boolean]
|
261
|
+
*/
|
262
|
+
|
263
|
+
static VALUE
|
264
|
+
rb_proj_angular_output (VALUE self, VALUE direction)
|
265
|
+
{
|
266
|
+
Proj *proj;
|
267
|
+
|
268
|
+
Data_Get_Struct(self, Proj, proj);
|
269
|
+
|
270
|
+
if ( rb_to_id(direction) == id_forward ) {
|
271
|
+
return proj_angular_output(proj->ref, PJ_FWD) == 1 ? Qtrue : Qfalse;
|
272
|
+
}
|
273
|
+
else if ( rb_to_id(direction) == id_inverse ) {
|
274
|
+
return proj_angular_output(proj->ref, PJ_INV) == 1 ? Qtrue : Qfalse;
|
275
|
+
}
|
276
|
+
else {
|
277
|
+
rb_raise(rb_eArgError, "invalid direction");
|
278
|
+
}
|
279
|
+
}
|
280
|
+
|
281
|
+
static VALUE
|
282
|
+
rb_proj_pj_info (VALUE self)
|
283
|
+
{
|
284
|
+
volatile VALUE vout;
|
285
|
+
Proj *proj;
|
286
|
+
PJ_PROJ_INFO info;
|
287
|
+
|
288
|
+
Data_Get_Struct(self, Proj, proj);
|
289
|
+
info = proj_pj_info(proj->ref);
|
290
|
+
|
291
|
+
vout = rb_hash_new();
|
292
|
+
rb_hash_aset(vout, rb_str_new2("id"), (info.id) ? rb_str_new2(info.id) : Qnil);
|
293
|
+
rb_hash_aset(vout, rb_str_new2("description"), rb_str_new2(info.description));
|
294
|
+
rb_hash_aset(vout, rb_str_new2("definition"), rb_str_new2(info.definition));
|
295
|
+
rb_hash_aset(vout, rb_str_new2("has_inverse"), INT2NUM(info.has_inverse));
|
296
|
+
rb_hash_aset(vout, rb_str_new2("accuracy"), rb_float_new(info.accuracy));
|
297
|
+
|
298
|
+
return vout;
|
299
|
+
}
|
300
|
+
|
301
|
+
|
302
|
+
static VALUE
|
303
|
+
rb_proj_factors (VALUE self, VALUE vlon, VALUE vlat)
|
304
|
+
{
|
305
|
+
Proj *proj;
|
306
|
+
PJ_COORD pos;
|
307
|
+
PJ_FACTORS factors;
|
308
|
+
|
309
|
+
Data_Get_Struct(self, Proj, proj);
|
310
|
+
|
311
|
+
pos.lp.lam = proj_torad(NUM2DBL(vlon));
|
312
|
+
pos.lp.phi = proj_torad(NUM2DBL(vlat));
|
313
|
+
|
314
|
+
factors = proj_factors(proj->ref, pos);
|
315
|
+
|
316
|
+
return rb_str_new((const char *) &factors, sizeof(PJ_FACTORS));
|
317
|
+
}
|
318
|
+
|
319
|
+
|
320
|
+
/*
|
321
|
+
Transforms coordinates forwardly from (lat1, lon1, z1) to (x1, y2, z2).
|
322
|
+
The order of coordinates arguments should be longitude, latitude, and height.
|
323
|
+
The input longitude and latitude should be in units 'degrees'.
|
324
|
+
|
325
|
+
@overload forward(lon1, lat1, z1 = nil)
|
326
|
+
@param lon1 [Numeric] longitude in degrees.
|
327
|
+
@param lat1 [Numeric] latitude in degrees.
|
328
|
+
@param z1 [Numeric, nil] vertical coordinate.
|
329
|
+
|
330
|
+
@return x2, y2[, z2]
|
331
|
+
|
332
|
+
@example
|
333
|
+
x2, y2 = pj.forward(lon1, lat1)
|
334
|
+
x2, y2, z2 = pj.forward(lon1, lat1, z1)
|
335
|
+
|
336
|
+
*/
|
337
|
+
static VALUE
|
338
|
+
rb_proj_forward (int argc, VALUE *argv, VALUE self)
|
339
|
+
{
|
340
|
+
volatile VALUE vlon, vlat, vz;
|
341
|
+
Proj *proj;
|
342
|
+
PJ_COORD data_in, data_out;
|
343
|
+
int errno;
|
344
|
+
|
345
|
+
rb_scan_args(argc, argv, "21", (VALUE*) &vlon, (VALUE*) &vlat, (VALUE*) &vz);
|
346
|
+
|
347
|
+
Data_Get_Struct(self, Proj, proj);
|
348
|
+
|
349
|
+
if ( ! proj->is_src_latlong ) {
|
350
|
+
rb_raise(rb_eRuntimeError, "requires latlong src crs. use #transform_forward instead of #forward.");
|
351
|
+
}
|
352
|
+
|
353
|
+
if ( proj_angular_input(proj->ref, PJ_FWD) == 1 ) {
|
354
|
+
data_in.lpz.lam = proj_torad(NUM2DBL(vlon));
|
355
|
+
data_in.lpz.phi = proj_torad(NUM2DBL(vlat));
|
356
|
+
data_in.lpz.z = NIL_P(vz) ? 0.0 : NUM2DBL(vz);
|
357
|
+
}
|
358
|
+
else {
|
359
|
+
data_in.xyz.x = NUM2DBL(vlon);
|
360
|
+
data_in.xyz.y = NUM2DBL(vlat);
|
361
|
+
data_in.xyz.z = NIL_P(vz) ? 0.0 : NUM2DBL(vz);
|
362
|
+
}
|
363
|
+
|
364
|
+
data_out = proj_trans(proj->ref, PJ_FWD, data_in);
|
365
|
+
|
366
|
+
if ( data_out.xyz.x == HUGE_VAL ) {
|
367
|
+
errno = proj_context_errno(PJ_DEFAULT_CTX);
|
368
|
+
rb_raise(rb_eRuntimeError, "%s", proj_errno_string(errno));
|
369
|
+
}
|
370
|
+
|
371
|
+
if ( NIL_P(vz) ) {
|
372
|
+
return rb_assoc_new(rb_float_new(data_out.xyz.x),
|
373
|
+
rb_float_new(data_out.xyz.y));
|
374
|
+
} else {
|
375
|
+
return rb_ary_new3(3, rb_float_new(data_out.xyz.x),
|
376
|
+
rb_float_new(data_out.xyz.y),
|
377
|
+
rb_float_new(data_out.xyz.z));
|
378
|
+
}
|
379
|
+
}
|
380
|
+
|
381
|
+
/*
|
382
|
+
Transforms coordinates inversely from (x1, y1, z1) to (lon2, lat2, z2).
|
383
|
+
The order of output coordinates is longitude, latitude and height.
|
384
|
+
The returned longitude and latitude are in units 'degrees'.
|
385
|
+
|
386
|
+
@overload inverse(x1, y1, z1 = nil)
|
387
|
+
@param x1 [Numeric]
|
388
|
+
@param y1 [Numeric]
|
389
|
+
@param z1 [Numeric, nil]
|
390
|
+
|
391
|
+
@return lon2, lat2, [, z2]
|
392
|
+
|
393
|
+
@example
|
394
|
+
lon2, lat2 = pj.inverse(x1, y1)
|
395
|
+
lon2, lat2, z2 = pj.inverse(x1, y1, z1)
|
396
|
+
|
397
|
+
*/
|
398
|
+
static VALUE
|
399
|
+
rb_proj_inverse (int argc, VALUE *argv, VALUE self)
|
400
|
+
{
|
401
|
+
volatile VALUE vx, vy, vz;
|
402
|
+
Proj *proj;
|
403
|
+
PJ_COORD data_in, data_out;
|
404
|
+
int errno;
|
405
|
+
|
406
|
+
rb_scan_args(argc, argv, "21", (VALUE *)&vx, (VALUE *)&vy, (VALUE *)&vz);
|
407
|
+
Data_Get_Struct(self, Proj, proj);
|
408
|
+
|
409
|
+
if ( ! proj->is_src_latlong ) {
|
410
|
+
rb_raise(rb_eRuntimeError, "requires latlong src crs. use #transform_inverse instead of #inverse.");
|
411
|
+
}
|
412
|
+
|
413
|
+
data_in.xyz.x = NUM2DBL(vx);
|
414
|
+
data_in.xyz.y = NUM2DBL(vy);
|
415
|
+
data_in.xyz.z = NIL_P(vz) ? 0.0 : NUM2DBL(vz);
|
416
|
+
|
417
|
+
data_out = proj_trans(proj->ref, PJ_INV, data_in);
|
418
|
+
|
419
|
+
if ( data_out.lpz.lam == HUGE_VAL ) {
|
420
|
+
errno = proj_errno(proj->ref);
|
421
|
+
rb_raise(rb_eRuntimeError, "%s", proj_errno_string(errno));
|
422
|
+
}
|
423
|
+
|
424
|
+
if ( proj_angular_output(proj->ref, PJ_INV) == 1 ) {
|
425
|
+
if ( NIL_P(vz) ) {
|
426
|
+
return rb_assoc_new(rb_float_new(proj_todeg(data_out.lpz.lam)),
|
427
|
+
rb_float_new(proj_todeg(data_out.lpz.phi)));
|
428
|
+
} else {
|
429
|
+
return rb_ary_new3(3, rb_float_new(proj_todeg(data_out.lpz.lam)),
|
430
|
+
rb_float_new(proj_todeg(data_out.lpz.phi)),
|
431
|
+
rb_float_new(data_out.lpz.z));
|
432
|
+
}
|
433
|
+
}
|
434
|
+
else {
|
435
|
+
if ( NIL_P(vz) ) {
|
436
|
+
return rb_assoc_new(rb_float_new(data_out.xyz.x),
|
437
|
+
rb_float_new(data_out.xyz.y));
|
438
|
+
} else {
|
439
|
+
return rb_ary_new3(3, rb_float_new(data_out.xyz.x),
|
440
|
+
rb_float_new(data_out.xyz.y),
|
441
|
+
rb_float_new(data_out.xyz.z));
|
442
|
+
}
|
443
|
+
}
|
444
|
+
}
|
445
|
+
|
446
|
+
static VALUE
|
447
|
+
rb_proj_transform_i (int argc, VALUE *argv, VALUE self, PJ_DIRECTION direction)
|
448
|
+
{
|
449
|
+
VALUE vx, vy, vz;
|
450
|
+
Proj *trans;
|
451
|
+
PJ_COORD c_in, c_out;
|
452
|
+
int errno;
|
453
|
+
|
454
|
+
rb_scan_args(argc, argv, "21", (VALUE*)&vx, (VALUE*)&vy, (VALUE*)&vz);
|
455
|
+
|
456
|
+
Data_Get_Struct(self, Proj, trans);
|
457
|
+
|
458
|
+
c_in.xyz.x = NUM2DBL(vx);
|
459
|
+
c_in.xyz.y = NUM2DBL(vy);
|
460
|
+
c_in.xyz.z = NIL_P(vz) ? 0.0 : NUM2DBL(vz);
|
461
|
+
|
462
|
+
c_out = proj_trans(trans->ref, direction, c_in);
|
463
|
+
|
464
|
+
if ( c_out.xyz.x == HUGE_VAL ) {
|
465
|
+
errno = proj_errno(trans->ref);
|
466
|
+
rb_raise(rb_eRuntimeError, "%s", proj_errno_string(errno));
|
467
|
+
}
|
468
|
+
|
469
|
+
if ( NIL_P(vz) ) {
|
470
|
+
return rb_ary_new3(2,
|
471
|
+
rb_float_new(c_out.xyz.x),
|
472
|
+
rb_float_new(c_out.xyz.y));
|
473
|
+
}
|
474
|
+
else {
|
475
|
+
return rb_ary_new3(3,
|
476
|
+
rb_float_new(c_out.xyz.x),
|
477
|
+
rb_float_new(c_out.xyz.y),
|
478
|
+
rb_float_new(c_out.xyz.z));
|
479
|
+
}
|
480
|
+
|
481
|
+
}
|
482
|
+
|
483
|
+
/*
|
484
|
+
Transforms coordinates forwardly from (x1, y1, z1) to (x1, y2, z2).
|
485
|
+
The order of coordinates arguments are according to source and target CRSs.
|
486
|
+
|
487
|
+
@overload transform_forward(x1, y1, z1 = nil)
|
488
|
+
@param x1 [Numeric]
|
489
|
+
@param y1 [Numeric]
|
490
|
+
@param z1 [Numeric, nil]
|
491
|
+
|
492
|
+
@return x2, y2[, z2] (Numeric)
|
493
|
+
|
494
|
+
@example
|
495
|
+
x2, y2 = pj.transform(x1, y1)
|
496
|
+
x2, y2, z2 = pj.transform(x1, y1, z1)
|
497
|
+
|
498
|
+
*/
|
499
|
+
static VALUE
|
500
|
+
rb_proj_transform_forward (int argc, VALUE *argv, VALUE self)
|
501
|
+
{
|
502
|
+
return rb_proj_transform_i(argc, argv, self, PJ_FWD);
|
503
|
+
}
|
504
|
+
|
505
|
+
/*
|
506
|
+
Transforms coordinates inversely from (x1, y1, z1) to (x1, y2, z2).
|
507
|
+
The order of coordinates arguments are according to source and target CRSs.
|
508
|
+
|
509
|
+
@overload transform_inverse(x1, y1, z1 = nil)
|
510
|
+
@param x1 [Numeric]
|
511
|
+
@param y1 [Numeric]
|
512
|
+
@param z1 [Numeric]
|
513
|
+
@return x2, y2[, z2] (Numeric)
|
514
|
+
|
515
|
+
@example
|
516
|
+
x2, y2 = pj.transform_inverse(x1, y1)
|
517
|
+
x2, y2, z2 = pj.transform_inverse(x1, y1, z1)
|
518
|
+
|
519
|
+
*/
|
520
|
+
static VALUE
|
521
|
+
rb_proj_transform_inverse (int argc, VALUE *argv, VALUE self)
|
522
|
+
{
|
523
|
+
return rb_proj_transform_i(argc, argv, self, PJ_INV);
|
524
|
+
}
|
525
|
+
|
526
|
+
/*
|
527
|
+
Constructs a CRS object with an argument.
|
528
|
+
The argument should be a String object one of
|
529
|
+
|
530
|
+
* a proj-string,
|
531
|
+
* a WKT string,
|
532
|
+
* an object code (like “EPSG:4326”, “urn:ogc:def:crs:EPSG::4326”,
|
533
|
+
“urn:ogc:def:coordinateOperation:EPSG::1671”),
|
534
|
+
* a OGC URN combining references for compound coordinate reference
|
535
|
+
systems (e.g “urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717” or
|
536
|
+
custom abbreviated syntax “EPSG:2393+5717”),
|
537
|
+
* a OGC URN combining references for concatenated operations (e.g.
|
538
|
+
“urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,
|
539
|
+
coordinateOperation:EPSG::1618”)
|
540
|
+
|
541
|
+
@overload initialize(definition)
|
542
|
+
@param definition [String] proj-string or other CRS definition (see above description).
|
543
|
+
|
544
|
+
@example
|
545
|
+
# Create a PROJ::CRS object
|
546
|
+
epsg_3857 = PROJ::CRS.new("EPSG:3857")
|
547
|
+
|
548
|
+
*/
|
549
|
+
|
550
|
+
static VALUE
|
551
|
+
rb_crs_initialize (VALUE self, VALUE vdef1)
|
552
|
+
{
|
553
|
+
Proj *proj;
|
554
|
+
PJ *ref;
|
555
|
+
int errno;
|
556
|
+
|
557
|
+
Data_Get_Struct(self, Proj, proj);
|
558
|
+
|
559
|
+
ref = proj_create(PJ_DEFAULT_CTX, StringValuePtr(vdef1));
|
560
|
+
|
561
|
+
if ( ! ref ) {
|
562
|
+
errno = proj_context_errno(PJ_DEFAULT_CTX);
|
563
|
+
rb_raise(rb_eRuntimeError, "%s", proj_errno_string(errno));
|
564
|
+
}
|
565
|
+
|
566
|
+
if ( proj_is_crs(ref) ) {
|
567
|
+
proj->ref = ref;
|
568
|
+
}
|
569
|
+
else {
|
570
|
+
rb_raise(rb_eRuntimeError, "should be crs definition");
|
571
|
+
}
|
572
|
+
|
573
|
+
return Qnil;
|
574
|
+
}
|
575
|
+
|
576
|
+
VALUE
|
577
|
+
rb_crs_new (PJ *ref)
|
578
|
+
{
|
579
|
+
volatile VALUE vcrs;
|
580
|
+
Proj *proj;
|
581
|
+
|
582
|
+
vcrs = rb_proj_s_allocate(rb_cCrs);
|
583
|
+
Data_Get_Struct(vcrs, Proj, proj);
|
584
|
+
|
585
|
+
proj->ref = ref;
|
586
|
+
|
587
|
+
return vcrs;
|
588
|
+
}
|
589
|
+
|
590
|
+
|
591
|
+
static VALUE
|
592
|
+
rb_proj_initialize_copy (VALUE self, VALUE obj)
|
593
|
+
{
|
594
|
+
Proj *proj, *other;
|
595
|
+
|
596
|
+
Data_Get_Struct(self, Proj, proj);
|
597
|
+
|
598
|
+
if ( rb_obj_is_kind_of(obj, rb_cProj) || rb_obj_is_kind_of(obj, rb_cCrs) ) {
|
599
|
+
Data_Get_Struct(obj, Proj, other);
|
600
|
+
proj->ref = proj_clone(PJ_DEFAULT_CTX, other->ref);
|
601
|
+
}
|
602
|
+
else {
|
603
|
+
rb_raise(rb_eArgError, "invalid class of argument object");
|
604
|
+
}
|
605
|
+
|
606
|
+
return self;
|
607
|
+
}
|
608
|
+
|
609
|
+
/*
|
610
|
+
Gets the name of the object.
|
611
|
+
|
612
|
+
@return [String]
|
613
|
+
*/
|
614
|
+
static VALUE
|
615
|
+
rb_proj_get_name (VALUE self)
|
616
|
+
{
|
617
|
+
Proj *proj;
|
618
|
+
|
619
|
+
Data_Get_Struct(self, Proj, proj);
|
620
|
+
|
621
|
+
return rb_str_new2(proj_get_name(proj->ref));
|
622
|
+
}
|
623
|
+
|
624
|
+
/*
|
625
|
+
Gets the authority name / codespace of an identifier of the object.
|
626
|
+
|
627
|
+
@overload id_auth_name (index=nil)
|
628
|
+
@param index [Integer] index of the identifier (0 = first identifier)
|
629
|
+
|
630
|
+
@return [String, nil]
|
631
|
+
*/
|
632
|
+
|
633
|
+
static VALUE
|
634
|
+
rb_proj_get_id_auth_name (int argc, VALUE *argv, VALUE self)
|
635
|
+
{
|
636
|
+
volatile VALUE vidx;
|
637
|
+
Proj *proj;
|
638
|
+
const char *string;
|
639
|
+
|
640
|
+
rb_scan_args(argc, argv, "01", (VALUE *)&vidx);
|
641
|
+
|
642
|
+
Data_Get_Struct(self, Proj, proj);
|
643
|
+
|
644
|
+
if ( NIL_P(vidx) ) {
|
645
|
+
string = proj_get_id_auth_name(proj->ref, 0);
|
646
|
+
}
|
647
|
+
else {
|
648
|
+
string = proj_get_id_auth_name(proj->ref, NUM2INT(vidx));
|
649
|
+
}
|
650
|
+
|
651
|
+
return (string) ? rb_str_new2(string) : Qnil;
|
652
|
+
}
|
653
|
+
|
654
|
+
/*
|
655
|
+
Gets the code of an identifier of the object.
|
656
|
+
|
657
|
+
@overload id_code (index=nil)
|
658
|
+
@param index [Integer] index of the identifier (0 = first identifier)
|
659
|
+
|
660
|
+
@return [String, nil]
|
661
|
+
*/
|
662
|
+
|
663
|
+
static VALUE
|
664
|
+
rb_proj_get_id_code (int argc, VALUE *argv, VALUE self)
|
665
|
+
{
|
666
|
+
volatile VALUE vidx;
|
667
|
+
Proj *proj;
|
668
|
+
const char *string;
|
669
|
+
|
670
|
+
rb_scan_args(argc, argv, "01", (VALUE *)&vidx);
|
671
|
+
|
672
|
+
Data_Get_Struct(self, Proj, proj);
|
673
|
+
|
674
|
+
if ( NIL_P(vidx) ) {
|
675
|
+
string = proj_get_id_code(proj->ref, 0);
|
676
|
+
}
|
677
|
+
else {
|
678
|
+
string = proj_get_id_code(proj->ref, NUM2INT(vidx));
|
679
|
+
}
|
680
|
+
|
681
|
+
return (string) ? rb_str_new2(string) : Qnil;
|
682
|
+
}
|
683
|
+
|
684
|
+
/*
|
685
|
+
Gets a PROJJSON string representation of the object.
|
686
|
+
|
687
|
+
This method may return nil if the object is not compatible
|
688
|
+
with an export to the requested type.
|
689
|
+
|
690
|
+
@return [String,nil]
|
691
|
+
*/
|
692
|
+
static VALUE
|
693
|
+
rb_proj_as_projjson (VALUE self)
|
694
|
+
{
|
695
|
+
Proj *proj;
|
696
|
+
const char *json;
|
697
|
+
|
698
|
+
Data_Get_Struct(self, Proj, proj);
|
699
|
+
json = proj_as_projjson(PJ_DEFAULT_CTX, proj->ref, NULL);
|
700
|
+
if ( ! json ) {
|
701
|
+
return Qnil;
|
702
|
+
}
|
703
|
+
return rb_str_new2(json);
|
704
|
+
}
|
705
|
+
|
706
|
+
|
707
|
+
/*
|
708
|
+
Gets a PROJ string representation of the object.
|
709
|
+
This method may return nil if the object is not compatible with
|
710
|
+
an export to the requested type.
|
711
|
+
|
712
|
+
@return [String,nil]
|
713
|
+
*/
|
714
|
+
static VALUE
|
715
|
+
rb_proj_as_proj_string (VALUE self)
|
716
|
+
{
|
717
|
+
Proj *proj;
|
718
|
+
const char *string;
|
719
|
+
|
720
|
+
Data_Get_Struct(self, Proj, proj);
|
721
|
+
string = proj_as_proj_string(PJ_DEFAULT_CTX, proj->ref, PJ_PROJ_5, NULL);
|
722
|
+
if ( ! string ) {
|
723
|
+
return Qnil;
|
724
|
+
}
|
725
|
+
return rb_str_new2(string);
|
726
|
+
}
|
727
|
+
|
728
|
+
static VALUE
|
729
|
+
rb_proj_ellipsoid_get_parameters (VALUE self)
|
730
|
+
{
|
731
|
+
Proj *proj;
|
732
|
+
PJ *ellps;
|
733
|
+
double a, b, invf;
|
734
|
+
int computed;
|
735
|
+
|
736
|
+
Data_Get_Struct(self, Proj, proj);
|
737
|
+
ellps = proj_get_ellipsoid(PJ_DEFAULT_CTX, proj->ref);
|
738
|
+
proj_ellipsoid_get_parameters(PJ_DEFAULT_CTX, ellps, &a, &b, &computed, &invf);
|
739
|
+
return rb_ary_new3(4,
|
740
|
+
rb_float_new(a),
|
741
|
+
rb_float_new(b),
|
742
|
+
INT2NUM(computed),
|
743
|
+
rb_float_new(invf));
|
744
|
+
}
|
745
|
+
|
746
|
+
/*
|
747
|
+
Gets a WKT expression of CRS definition of the object.
|
748
|
+
|
749
|
+
@return [String]
|
750
|
+
*/
|
751
|
+
static VALUE
|
752
|
+
rb_proj_as_wkt (int argc, VALUE *argv, VALUE self)
|
753
|
+
{
|
754
|
+
volatile VALUE vidx;
|
755
|
+
Proj *proj;
|
756
|
+
const char *wkt;
|
757
|
+
|
758
|
+
rb_scan_args(argc, argv, "01", (VALUE *)&vidx);
|
759
|
+
|
760
|
+
Data_Get_Struct(self, Proj, proj);
|
761
|
+
|
762
|
+
if ( NIL_P(vidx) ) {
|
763
|
+
wkt = proj_as_wkt(PJ_DEFAULT_CTX, proj->ref, PJ_WKT2_2018, NULL);
|
764
|
+
}
|
765
|
+
else {
|
766
|
+
wkt = proj_as_wkt(PJ_DEFAULT_CTX, proj->ref, NUM2INT(vidx), NULL);
|
767
|
+
}
|
768
|
+
|
769
|
+
if ( ! wkt ) {
|
770
|
+
return Qnil;
|
771
|
+
}
|
772
|
+
else {
|
773
|
+
return rb_str_new2(wkt);
|
774
|
+
}
|
775
|
+
}
|
776
|
+
|
777
|
+
|
778
|
+
void
|
779
|
+
Init_simple_proj ()
|
780
|
+
{
|
781
|
+
id_forward = rb_intern("forward");
|
782
|
+
id_inverse = rb_intern("inverse");
|
783
|
+
|
784
|
+
rb_cProj = rb_define_class("PROJ", rb_cObject);
|
785
|
+
rb_cCrs = rb_define_class_under(rb_cProj, "CRS", rb_cObject);
|
786
|
+
rb_mCommon = rb_define_module_under(rb_cProj, "Common");
|
787
|
+
|
788
|
+
rb_include_module(rb_cProj, rb_mCommon);
|
789
|
+
rb_include_module(rb_cCrs, rb_mCommon);
|
790
|
+
|
791
|
+
rb_define_singleton_method(rb_cProj, "_info", rb_proj_info, 0);
|
792
|
+
|
793
|
+
rb_define_alloc_func(rb_cProj, rb_proj_s_allocate);
|
794
|
+
rb_define_method(rb_cProj, "initialize", rb_proj_initialize, -1);
|
795
|
+
rb_define_method(rb_cProj, "source_crs", rb_proj_source_crs, 0);
|
796
|
+
rb_define_method(rb_cProj, "target_crs", rb_proj_target_crs, 0);
|
797
|
+
rb_define_method(rb_cProj, "angular_input?", rb_proj_angular_input, 1);
|
798
|
+
rb_define_method(rb_cProj, "angular_output?", rb_proj_angular_output, 1);
|
799
|
+
rb_define_method(rb_cProj, "normalize_for_visualization", rb_proj_normalize_for_visualization, 0);
|
800
|
+
rb_define_method(rb_cProj, "forward", rb_proj_forward, -1);
|
801
|
+
rb_define_method(rb_cProj, "inverse", rb_proj_inverse, -1);
|
802
|
+
rb_define_method(rb_cProj, "transform", rb_proj_transform_forward, -1);
|
803
|
+
rb_define_method(rb_cProj, "transform_inverse", rb_proj_transform_inverse, -1);
|
804
|
+
rb_define_private_method(rb_cProj, "_pj_info", rb_proj_pj_info, 0);
|
805
|
+
rb_define_private_method(rb_cProj, "_factors", rb_proj_factors, 2);
|
806
|
+
|
807
|
+
rb_define_alloc_func(rb_cCrs, rb_proj_s_allocate);
|
808
|
+
rb_define_method(rb_cCrs, "initialize", rb_crs_initialize, 1);
|
809
|
+
rb_define_method(rb_cCrs, "normalize_for_visualization", rb_proj_normalize_for_visualization, 0);
|
810
|
+
|
811
|
+
rb_define_method(rb_mCommon, "initialize_copy", rb_proj_initialize_copy, 1);
|
812
|
+
rb_define_method(rb_mCommon, "name", rb_proj_get_name, 0);
|
813
|
+
rb_define_method(rb_mCommon, "id_auth_name", rb_proj_get_id_auth_name, -1);
|
814
|
+
rb_define_method(rb_mCommon, "id_code", rb_proj_get_id_code, -1);
|
815
|
+
rb_define_method(rb_mCommon, "to_proj_string", rb_proj_as_proj_string, 0);
|
816
|
+
rb_define_method(rb_mCommon, "to_projjson", rb_proj_as_projjson, 0);
|
817
|
+
rb_define_method(rb_mCommon, "ellipsoid_parameters", rb_proj_ellipsoid_get_parameters, 0);
|
818
|
+
rb_define_method(rb_mCommon, "to_wkt", rb_proj_as_wkt, -1);
|
819
|
+
|
820
|
+
rb_define_const(rb_cProj, "WKT2_2015", INT2NUM(PJ_WKT2_2015));
|
821
|
+
rb_define_const(rb_cProj, "WKT2_2015_SIMPLIFIED", INT2NUM(PJ_WKT2_2015_SIMPLIFIED));
|
822
|
+
#ifdef WKT2_2018
|
823
|
+
rb_define_const(rb_cProj, "WKT2_2018", INT2NUM(PJ_WKT2_2018));
|
824
|
+
rb_define_const(rb_cProj, "WKT2_2018_SIMPLIFIED", INT2NUM(PJ_WKT2_2018_SIMPLIFIED));
|
825
|
+
#endif
|
826
|
+
#ifdef WKT2_2019
|
827
|
+
rb_define_const(rb_cProj, "WKT2_2019", INT2NUM(PJ_WKT2_2019));
|
828
|
+
rb_define_const(rb_cProj, "WKT2_2019_SIMPLIFIED", INT2NUM(PJ_WKT2_2019_SIMPLIFIED));
|
829
|
+
#endif
|
830
|
+
rb_define_const(rb_cProj, "WKT1_GDAL", INT2NUM(PJ_WKT1_GDAL));
|
831
|
+
rb_define_const(rb_cProj, "WKT1_ESRI", INT2NUM(PJ_WKT1_ESRI));
|
832
|
+
}
|
data/ext/rb_proj.h
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#ifndef RB_PROJ_H
|
2
|
+
#define RB_PROJ_H
|
3
|
+
|
4
|
+
#include <proj.h>
|
5
|
+
|
6
|
+
typedef struct {
|
7
|
+
PJ *ref;
|
8
|
+
int is_src_latlong;
|
9
|
+
} Proj;
|
10
|
+
|
11
|
+
extern PJ* PJ_DEFAULT_LONGLAT;
|
12
|
+
|
13
|
+
extern VALUE rb_cProj;
|
14
|
+
extern VALUE rb_cCrs;
|
15
|
+
|
16
|
+
VALUE rb_crs_new(PJ *);
|
17
|
+
|
18
|
+
#endif
|
data/lib/simple-proj.rb
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'simple_proj.so'
|
2
|
+
require 'json'
|
3
|
+
require 'bindata'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
class PROJ
|
7
|
+
|
8
|
+
# Returns PROJ info
|
9
|
+
#
|
10
|
+
# @return [OpenStruct]
|
11
|
+
def self.info
|
12
|
+
return OpenStruct.new(_info)
|
13
|
+
end
|
14
|
+
|
15
|
+
alias transform_forward transform
|
16
|
+
|
17
|
+
alias forward_lonlat forward
|
18
|
+
|
19
|
+
# A variant of #forward which accept the axis order as (lat, lon).
|
20
|
+
def forward_latlon (lat, lon, z = nil)
|
21
|
+
return forward(lon, lat, z)
|
22
|
+
end
|
23
|
+
|
24
|
+
alias inverse_lonlat inverse
|
25
|
+
|
26
|
+
# A variant of #inverse which return the output with the axis order in (lat, lon).
|
27
|
+
def inverse_latlon (x, y, z = nil)
|
28
|
+
return inverse_latlon(x, y, z)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns a internal information of the object
|
32
|
+
#
|
33
|
+
# @return [OpenStruct]
|
34
|
+
def pj_info
|
35
|
+
info = _pj_info
|
36
|
+
if info["id"] == "unknown"
|
37
|
+
transform(0,0)
|
38
|
+
info = _pj_info
|
39
|
+
if info["id"] == "unknown"
|
40
|
+
return OpenStruct.new(info)
|
41
|
+
else
|
42
|
+
return pj_info
|
43
|
+
end
|
44
|
+
else
|
45
|
+
info["definition"] = info["definition"].strip.split(/\s+/).map{|s| "+"+s}.join(" ")
|
46
|
+
return OpenStruct.new(info)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns a definition of the object
|
51
|
+
#
|
52
|
+
# @return [OpenStruct]
|
53
|
+
def definition
|
54
|
+
return pj_info.definition
|
55
|
+
end
|
56
|
+
|
57
|
+
ENDIAN = ( [1].pack("I") == [1].pack("N") ) ? :big : :little
|
58
|
+
|
59
|
+
# A class represents PJ_FACTORS structure defined using BinData::Record
|
60
|
+
#
|
61
|
+
# This structure has members,
|
62
|
+
#
|
63
|
+
# * meridional_scale
|
64
|
+
# * parallel_scale
|
65
|
+
# * areal_scale
|
66
|
+
# * angular_distortion
|
67
|
+
# * meridian_parallel_angle
|
68
|
+
# * meridian_convergence
|
69
|
+
# * tissot_semimajor
|
70
|
+
# * tissot_semiminor
|
71
|
+
# * dx_dlam
|
72
|
+
# * dx_dphi
|
73
|
+
# * dy_dlam
|
74
|
+
# * dy_dphi
|
75
|
+
#
|
76
|
+
class FACTORS < BinData::Record
|
77
|
+
endian ENDIAN
|
78
|
+
double :meridional_scale
|
79
|
+
double :parallel_scale
|
80
|
+
double :areal_scale
|
81
|
+
double :angular_distortion
|
82
|
+
double :meridian_parallel_angle
|
83
|
+
double :meridian_convergence
|
84
|
+
double :tissot_semimajor
|
85
|
+
double :tissot_semiminor
|
86
|
+
double :dx_dlam
|
87
|
+
double :dx_dphi
|
88
|
+
double :dy_dlam
|
89
|
+
double :dy_dphi
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns PROJ::FACTORS object
|
93
|
+
#
|
94
|
+
# @retrun [PROJ::FACTORS]
|
95
|
+
def factors (lon, lat)
|
96
|
+
return FACTORS.read(_factors(lon, lat))
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
class PROJ
|
102
|
+
|
103
|
+
module Common
|
104
|
+
|
105
|
+
# Gets a EPSG code of the object
|
106
|
+
#
|
107
|
+
# @return [String, nil]
|
108
|
+
def to_epsg_code
|
109
|
+
auth = id_auth_name
|
110
|
+
code = id_code
|
111
|
+
if auth and code
|
112
|
+
return auth + ":" + code
|
113
|
+
else
|
114
|
+
return nil
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Gets a Hash object parsed from the PROJJSON expression of the object
|
119
|
+
#
|
120
|
+
# @return [Hash, nil]
|
121
|
+
def to_projjson_as_hash
|
122
|
+
json = to_projjson
|
123
|
+
if json
|
124
|
+
return JSON.parse(json)
|
125
|
+
else
|
126
|
+
return nil
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def to_wkt2_2015
|
131
|
+
return to_wkt(WKT2_2015)
|
132
|
+
end
|
133
|
+
|
134
|
+
def to_wkt2_2015_simplified
|
135
|
+
return to_wkt(WKT2_2015_SIMPLIFIED)
|
136
|
+
end
|
137
|
+
|
138
|
+
def to_wkt2_2018
|
139
|
+
return to_wkt(WKT2_2018)
|
140
|
+
end
|
141
|
+
|
142
|
+
def to_wkt2_2018_simplified
|
143
|
+
if defined? WKT2_2018_SIMPLIFIED
|
144
|
+
return to_wkt(WKT2_2018_SIMPLIFIED)
|
145
|
+
else
|
146
|
+
raise "WKT2_2018 not defined. Check PROJ version."
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def to_wkt_gdal
|
151
|
+
return to_wkt(WKT1_GDAL)
|
152
|
+
end
|
153
|
+
|
154
|
+
def to_wkt_esri
|
155
|
+
return to_wkt(WKT1_ESRI)
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
begin
|
163
|
+
require "simple-proj-carray"
|
164
|
+
rescue LoadError
|
165
|
+
end
|
data/simple-proj.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
Gem::Specification::new do |s|
|
3
|
+
version = "1.0.0"
|
4
|
+
|
5
|
+
files = Dir.glob("**/*") - [
|
6
|
+
Dir.glob("simple-proj-*.gem"),
|
7
|
+
Dir.glob("doc/**/*"),
|
8
|
+
Dir.glob("examples/**/*"),
|
9
|
+
].flatten
|
10
|
+
|
11
|
+
s.platform = Gem::Platform::RUBY
|
12
|
+
s.name = "simple-proj"
|
13
|
+
s.summary = "Ruby extension library for PROJ 7"
|
14
|
+
s.description = <<-HERE
|
15
|
+
Ruby extension library for PROJ 7
|
16
|
+
HERE
|
17
|
+
s.version = version
|
18
|
+
s.license = 'MIT'
|
19
|
+
s.author = "Hiroki Motoyoshi"
|
20
|
+
s.email = ""
|
21
|
+
s.homepage = 'https://github.com/himotoyoshi/simple-proj'
|
22
|
+
s.files = files
|
23
|
+
s.extensions = [ "ext/extconf.rb" ]
|
24
|
+
s.required_ruby_version = ">= 2.4.0"
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple-proj
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Hiroki Motoyoshi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-10-22 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: " Ruby extension library for PROJ 7\n"
|
14
|
+
email: ''
|
15
|
+
executables: []
|
16
|
+
extensions:
|
17
|
+
- ext/extconf.rb
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- API.md
|
21
|
+
- README.md
|
22
|
+
- Rakefile
|
23
|
+
- ext/extconf.rb
|
24
|
+
- ext/rb_proj.c
|
25
|
+
- ext/rb_proj.h
|
26
|
+
- lib/simple-proj.rb
|
27
|
+
- simple-proj.gemspec
|
28
|
+
homepage: https://github.com/himotoyoshi/simple-proj
|
29
|
+
licenses:
|
30
|
+
- MIT
|
31
|
+
metadata: {}
|
32
|
+
post_install_message:
|
33
|
+
rdoc_options: []
|
34
|
+
require_paths:
|
35
|
+
- lib
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.4.0
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
requirements: []
|
47
|
+
rubyforge_project:
|
48
|
+
rubygems_version: 2.7.6
|
49
|
+
signing_key:
|
50
|
+
specification_version: 4
|
51
|
+
summary: Ruby extension library for PROJ 7
|
52
|
+
test_files: []
|