16
16
+----------------------------------------------------------------------+
17
17
*/
18
18
19
+
19
20
/* $Id$ */
20
21
21
22
#ifdef HAVE_CONFIG_H
@@ -35,12 +36,57 @@ ZEND_BEGIN_ARG_INFO_EX(haversine_args, 0, 0, 4)
35
36
ZEND_ARG_INFO (0 , radius )
36
37
ZEND_END_ARG_INFO ()
37
38
39
+ ZEND_BEGIN_ARG_INFO_EX (helmert_args , 0 , 0 , 3 )
40
+ ZEND_ARG_INFO (0 , x )
41
+ ZEND_ARG_INFO (0 , y )
42
+ ZEND_ARG_INFO (0 , z )
43
+ ZEND_END_ARG_INFO ()
44
+
45
+ ZEND_BEGIN_ARG_INFO_EX (polar_to_cartesian_args , 0 , 0 , 3 )
46
+ ZEND_ARG_INFO (0 , latitude )
47
+ ZEND_ARG_INFO (0 , longitude )
48
+ ZEND_ARG_INFO (0 , reference_ellipsoid )
49
+ ZEND_END_ARG_INFO ()
50
+
51
+ ZEND_BEGIN_ARG_INFO_EX (cartesian_to_polar_args , 0 , 0 , 4 )
52
+ ZEND_ARG_INFO (0 , x )
53
+ ZEND_ARG_INFO (0 , y )
54
+ ZEND_ARG_INFO (0 , z )
55
+ ZEND_ARG_INFO (0 , reference_ellipsoid )
56
+ ZEND_END_ARG_INFO ()
57
+
58
+ ZEND_BEGIN_ARG_INFO_EX (transform_datum_args , 0 , 0 , 4 )
59
+ ZEND_ARG_INFO (0 , latitude )
60
+ ZEND_ARG_INFO (0 , longitude )
61
+ ZEND_ARG_INFO (0 , from_reference_ellipsoid )
62
+ ZEND_ARG_INFO (0 , to_reference_ellipsoid )
63
+ ZEND_END_ARG_INFO ()
64
+
65
+ ZEND_BEGIN_ARG_INFO_EX (dms_to_decimal_args , 0 , 0 , 4 )
66
+ ZEND_ARG_INFO (0 , degrees )
67
+ ZEND_ARG_INFO (0 , minutes )
68
+ ZEND_ARG_INFO (0 , seconds )
69
+ ZEND_ARG_INFO (0 , direction )
70
+ ZEND_END_ARG_INFO ()
71
+
72
+ ZEND_BEGIN_ARG_INFO_EX (decimal_to_dms_args , 0 , 0 , 2 )
73
+ ZEND_ARG_INFO (0 , decimal )
74
+ ZEND_ARG_INFO (0 , coordinate )
75
+ ZEND_END_ARG_INFO ()
76
+
38
77
/* {{{ geospatial_functions[]
39
78
*
40
79
* Every user visible function must have an entry in geospatial_functions[].
41
80
*/
42
81
const zend_function_entry geospatial_functions [] = {
43
82
PHP_FE (haversine , haversine_args )
83
+ PHP_FE (helmert , helmert_args )
84
+ PHP_FE (polar_to_cartesian , polar_to_cartesian_args )
85
+ PHP_FE (cartesian_to_polar , cartesian_to_polar_args )
86
+ PHP_FE (transform_datum , transform_datum_args )
87
+ PHP_FE (dms_to_decimal , dms_to_decimal_args )
88
+ PHP_FE (decimal_to_dms , decimal_to_dms_args )
89
+ /* End of functions */
44
90
{ NULL , NULL , NULL }
45
91
};
46
92
/* }}} */
@@ -75,6 +121,8 @@ PHP_MINIT_FUNCTION(geospatial)
75
121
{
76
122
REGISTER_DOUBLE_CONSTANT ("GEO_DEG_TO_RAD" , GEO_DEG_TO_RAD , CONST_CS | CONST_PERSISTENT );
77
123
REGISTER_DOUBLE_CONSTANT ("GEO_EARTH_RADIUS" , GEO_EARTH_RADIUS , CONST_CS | CONST_PERSISTENT );
124
+ REGISTER_LONG_CONSTANT ("GEO_AIRY_1830" , GEO_AIRY_1830 , CONST_CS | CONST_PERSISTENT );
125
+ REGISTER_LONG_CONSTANT ("GEO_WGS84" , GEO_WGS84 , CONST_CS | CONST_PERSISTENT );
78
126
return SUCCESS ;
79
127
}
80
128
/* }}} */
@@ -97,6 +145,252 @@ PHP_MINFO_FUNCTION(geospatial)
97
145
}
98
146
/* }}} */
99
147
148
+ geo_ellipsoid get_ellipsoid (long ellipsoid_const )
149
+ {
150
+ switch (ellipsoid_const ) {
151
+ case GEO_AIRY_1830 :
152
+ return airy_1830 ;
153
+ break ;
154
+ case GEO_WGS84 :
155
+ default :
156
+ return wgs84 ;
157
+ break ;
158
+ }
159
+ }
160
+
161
+ geo_helmert_constants get_helmert_constants (long from , long to )
162
+ {
163
+ switch (from - to ) {
164
+ case 1 :
165
+ return osgb36_wgs84 ;
166
+ break ;
167
+ default :
168
+ case -1 :
169
+ return wgs84_osgb36 ;
170
+ break ;
171
+ }
172
+ }
173
+
174
+ geo_cartesian helmert (double x , double y , double z , geo_helmert_constants helmet_consts )
175
+ {
176
+ double rX , rY , rZ ;
177
+ double xOut , yOut , zOut ;
178
+ double scale_change ;
179
+ geo_cartesian point ;
180
+ scale_change = 1 + (helmet_consts .scale_change );
181
+ rX = helmet_consts .rotation_x / GEO_SEC_IN_DEG * GEO_DEG_TO_RAD ;
182
+ rY = helmet_consts .rotation_y / GEO_SEC_IN_DEG * GEO_DEG_TO_RAD ;
183
+ rZ = helmet_consts .rotation_z / GEO_SEC_IN_DEG * GEO_DEG_TO_RAD ;
184
+
185
+ xOut = helmet_consts .translation_x + ((x - (rZ * y ) + (rY * z )) * scale_change );
186
+
187
+ yOut = helmet_consts .translation_y + (((rZ * x ) + y - (rX * z )) * scale_change );
188
+
189
+ zOut = helmet_consts .translation_z + (((-1 * rY * x ) + (rX * y ) + z ) * scale_change );
190
+
191
+ point .x = xOut ;
192
+ point .y = yOut ;
193
+ point .z = zOut ;
194
+ return point ;
195
+ }
196
+
197
+ geo_cartesian polar_to_cartesian (double latitude , double longitude , geo_ellipsoid eli )
198
+ {
199
+ double x , y , z ;
200
+
201
+ geo_cartesian point ;
202
+ double phi = latitude * GEO_DEG_TO_RAD ;
203
+ double lambda = longitude * GEO_DEG_TO_RAD ;
204
+ double eSq = ((eli .a * eli .a ) - (eli .b * eli .b )) / (eli .a * eli .a );
205
+ double nu = eli .a / sqrt (1 - (eSq * sin (phi ) * sin (phi )));
206
+ x = nu + HEIGHT ;
207
+ x *= cos (phi ) * cos (lambda );
208
+ y = nu + HEIGHT ;
209
+ y *= cos (phi ) * sin (lambda );
210
+ z = ((1 - eSq ) * nu ) + HEIGHT ;
211
+ z *= sin (phi );
212
+ point .x = x ;
213
+ point .y = y ;
214
+ point .z = z ;
215
+ return point ;
216
+ }
217
+
218
+
219
+ geo_lat_long cartesian_to_polar (double x , double y , double z , geo_ellipsoid eli )
220
+ {
221
+
222
+ double latitude , longitude ;
223
+ double nu , lambda , h ;
224
+ geo_lat_long polar ;
225
+
226
+ /* aiming for 1m accuracy */
227
+ double precision = 0.1 / eli .a ;
228
+ double eSq = ((eli .a * eli .a ) - (eli .b * eli .b )) / (eli .a * eli .a );
229
+ double p = sqrt (x * x + y * y );
230
+ double phi = atan2 (z , p * (1 - eSq ));
231
+ double phiP = 2 * M_PI ;
232
+
233
+ while (abs (phi - phiP ) > precision ) {
234
+ nu = eli .a / sqrt (1 - eSq * sin (phi ) * sin (phi ));
235
+ phiP = phi ;
236
+ phi = atan2 (z + eSq * nu * sin (phi ), p );
237
+ }
238
+
239
+ lambda = atan2 (y ,x );
240
+ h = p / cos (phi ) - nu ;
241
+ polar .latitude = phi / GEO_DEG_TO_RAD ;
242
+ polar .longitude = lambda / GEO_DEG_TO_RAD ;
243
+ polar .height = h ;
244
+
245
+ return polar ;
246
+ }
247
+
248
+ /* {{{ proto dms_to_decimal(double degrees, double minutes, double seconds [,string direction])
249
+ * Convert degrees, minutes & seconds values to decimal degrees */
250
+ PHP_FUNCTION (dms_to_decimal )
251
+ {
252
+ double degrees , minutes , sign ;
253
+ double seconds , decimal ;
254
+ char * direction = "" ;
255
+ int direction_len ;
256
+
257
+ if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC , "ddd|s" , & degrees , & minutes , & seconds , & direction , & direction_len ) == FAILURE ) {
258
+ return ;
259
+ }
260
+
261
+ if (strcmp ("" , direction ) == 0 ) {
262
+ sign = degrees > 1 ? 1 : -1 ;
263
+ } else {
264
+ sign = strcmp (direction , "S" ) == 0 || strcmp (direction , "W" ) == 0 ? -1 : 1 ;
265
+ }
266
+
267
+ decimal = abs (degrees ) + minutes / 60 + seconds / 3600 ;
268
+ decimal *= sign ;
269
+ RETURN_DOUBLE (decimal );
270
+ }
271
+ /* }}} */
272
+
273
+ /* {{{ proto decimal_to_dms(double decimal, string coordinate)
274
+ * Convert decimal degrees value to whole degrees and minutes and decimal seconds */
275
+ PHP_FUNCTION (decimal_to_dms )
276
+ {
277
+ double decimal , seconds ;
278
+ int degrees , minutes ;
279
+ char * direction ;
280
+ char * coordinate ;
281
+ int coordinate_len ;
282
+
283
+ if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC , "ds" , & decimal , & coordinate , & coordinate_len ) == FAILURE ) {
284
+ return ;
285
+ }
286
+
287
+ if (strcmp (coordinate , "longitude" ) == 0 ) {
288
+ direction = decimal < 1 ? "W" : "E" ;
289
+ } else {
290
+ direction = decimal < 1 ? "S" : "N" ;
291
+ }
292
+
293
+ array_init (return_value );
294
+ decimal = fabs (decimal );
295
+ degrees = (int ) decimal ;
296
+ minutes = decimal * 60 - degrees * 60 ;
297
+ seconds = decimal * 3600 - degrees * 3600 - minutes * 60 ;
298
+ add_assoc_long (return_value , "degrees" , degrees );
299
+ add_assoc_long (return_value , "minutes" , minutes );
300
+ add_assoc_double (return_value , "seconds" , seconds );
301
+ add_assoc_string (return_value , "direction" , direction , 1 );
302
+ }
303
+ /* }}} */
304
+
305
+ /* {{{ proto helmert(double x, double y, double z [, long from_reference_ellipsoid, long to_reference_ellipsoid])
306
+ * Convert polar ones (latitude, longitude) tp cartesian co-ordiantes (x, y, z) */
307
+ PHP_FUNCTION (helmert )
308
+ {
309
+ double x , y , z ;
310
+ geo_cartesian point ;
311
+ long from_reference_ellipsoid , to_reference_ellipsoid ;
312
+ if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC , "ddd|ll" , & x , & y , & z , & from_reference_ellipsoid , & to_reference_ellipsoid ) == FAILURE ) {
313
+ return ;
314
+ }
315
+
316
+ array_init (return_value );
317
+ geo_helmert_constants helmert_constants = get_helmert_constants (from_reference_ellipsoid , to_reference_ellipsoid );
318
+ point = helmert (x , y , z , helmert_constants );
319
+ add_assoc_double (return_value , "x" , point .x );
320
+ add_assoc_double (return_value , "y" , point .y );
321
+ add_assoc_double (return_value , "z" , point .z );
322
+ }
323
+ /* }}} */
324
+
325
+ /* {{{ proto polar_to_cartesian(double latitude, double longitude[, long reference_ellipsoid])
326
+ * Convert polar ones (latitude, longitude) tp cartesian co-ordiantes (x, y, z) */
327
+ PHP_FUNCTION (polar_to_cartesian )
328
+ {
329
+ double latitude , longitude ;
330
+ long reference_ellipsoid ;
331
+ geo_cartesian point ;
332
+
333
+ if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC , "dd|l" , & latitude , & longitude , & reference_ellipsoid ) == FAILURE ) {
334
+ return ;
335
+ }
336
+
337
+ geo_ellipsoid eli = get_ellipsoid (reference_ellipsoid );
338
+ array_init (return_value );
339
+ point = polar_to_cartesian (latitude , longitude , eli );
340
+ add_assoc_double (return_value , "x" , point .x );
341
+ add_assoc_double (return_value , "y" , point .y );
342
+ add_assoc_double (return_value , "z" , point .z );
343
+ }
344
+ /* }}} */
345
+
346
+ /* {{{ proto cartesian_to_polar(double x, double y, double z [, long reference_ellipsoid])
347
+ * Convert cartesian co-ordiantes (x, y, z) to polar ones (latitude, longitude) */
348
+ PHP_FUNCTION (cartesian_to_polar )
349
+ {
350
+ double x , y , z ;
351
+ long reference_ellipsoid ;
352
+ geo_lat_long polar ;
353
+
354
+ if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC , "ddd|l" , & x , & y , & z , & reference_ellipsoid ) == FAILURE ) {
355
+ return ;
356
+ }
357
+
358
+ geo_ellipsoid eli = get_ellipsoid (reference_ellipsoid );
359
+ array_init (return_value );
360
+ polar = cartesian_to_polar (x , y , z , eli );
361
+ add_assoc_double (return_value , "lat" , polar .latitude );
362
+ add_assoc_double (return_value , "long" , polar .longitude );
363
+ add_assoc_double (return_value , "height" , polar .height );
364
+ }
365
+ /* }}} */
366
+
367
+
368
+ /* {{{ proto transform_datum(double latitude, double longitude, long from_reference_ellipsoid, long to_reference_ellipsoid)
369
+ * Unified function to transform projection of geo-cordinates between datums */
370
+ PHP_FUNCTION (transform_datum )
371
+ {
372
+ double latitude , longitude ;
373
+ long from_reference_ellipsoid , to_reference_ellipsoid ;
374
+ geo_cartesian point , converted_point ;
375
+ geo_lat_long polar ;
376
+ if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC , "ddll" , & latitude , & longitude , & from_reference_ellipsoid , & to_reference_ellipsoid ) == FAILURE ) {
377
+ return ;
378
+ }
379
+
380
+ geo_ellipsoid eli_from = get_ellipsoid (from_reference_ellipsoid );
381
+ geo_ellipsoid eli_to = get_ellipsoid (to_reference_ellipsoid );
382
+ point = polar_to_cartesian (latitude , longitude , eli_from );
383
+ geo_helmert_constants helmert_constants = get_helmert_constants (from_reference_ellipsoid , to_reference_ellipsoid );
384
+ converted_point = helmert (point .x , point .y , point .z , helmert_constants );
385
+ polar = cartesian_to_polar (converted_point .x , converted_point .y , converted_point .z , eli_to );
386
+
387
+ array_init (return_value );
388
+ add_assoc_double (return_value , "lat" , polar .latitude );
389
+ add_assoc_double (return_value , "long" , polar .longitude );
390
+ add_assoc_double (return_value , "height" , polar .height );
391
+ }
392
+ /* }}} */
393
+
100
394
/* {{{ proto haversine(double fromLat, double fromLong, double toLat, double toLong [, double radius ])
101
395
* Calculates the greater circle distance between the two lattitude/longitude pairs */
102
396
PHP_FUNCTION (haversine )
0 commit comments