simple-proj 1.0.0
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/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: []
|