16
16
+----------------------------------------------------------------------+
17
17
*/
18
18
19
+
19
20
/* $Id$ */
20
21
21
22
#ifdef HAVE_CONFIG_H
@@ -42,13 +43,58 @@ ZEND_BEGIN_ARG_INFO_EX(fraction_along_gc_line_args, 0, 0, 3)
42
43
ZEND_ARG_INFO (0 , radius )
43
44
ZEND_END_ARG_INFO ()
44
45
46
+ ZEND_BEGIN_ARG_INFO_EX (helmert_args , 0 , 0 , 3 )
47
+ ZEND_ARG_INFO (0 , x )
48
+ ZEND_ARG_INFO (0 , y )
49
+ ZEND_ARG_INFO (0 , z )
50
+ ZEND_END_ARG_INFO ()
51
+
52
+ ZEND_BEGIN_ARG_INFO_EX (polar_to_cartesian_args , 0 , 0 , 3 )
53
+ ZEND_ARG_INFO (0 , latitude )
54
+ ZEND_ARG_INFO (0 , longitude )
55
+ ZEND_ARG_INFO (0 , reference_ellipsoid )
56
+ ZEND_END_ARG_INFO ()
57
+
58
+ ZEND_BEGIN_ARG_INFO_EX (cartesian_to_polar_args , 0 , 0 , 4 )
59
+ ZEND_ARG_INFO (0 , x )
60
+ ZEND_ARG_INFO (0 , y )
61
+ ZEND_ARG_INFO (0 , z )
62
+ ZEND_ARG_INFO (0 , reference_ellipsoid )
63
+ ZEND_END_ARG_INFO ()
64
+
65
+ ZEND_BEGIN_ARG_INFO_EX (transform_datum_args , 0 , 0 , 4 )
66
+ ZEND_ARG_INFO (0 , latitude )
67
+ ZEND_ARG_INFO (0 , longitude )
68
+ ZEND_ARG_INFO (0 , from_reference_ellipsoid )
69
+ ZEND_ARG_INFO (0 , to_reference_ellipsoid )
70
+ ZEND_END_ARG_INFO ()
71
+
72
+ ZEND_BEGIN_ARG_INFO_EX (dms_to_decimal_args , 0 , 0 , 4 )
73
+ ZEND_ARG_INFO (0 , degrees )
74
+ ZEND_ARG_INFO (0 , minutes )
75
+ ZEND_ARG_INFO (0 , seconds )
76
+ ZEND_ARG_INFO (0 , direction )
77
+ ZEND_END_ARG_INFO ()
78
+
79
+ ZEND_BEGIN_ARG_INFO_EX (decimal_to_dms_args , 0 , 0 , 2 )
80
+ ZEND_ARG_INFO (0 , decimal )
81
+ ZEND_ARG_INFO (0 , coordinate )
82
+ ZEND_END_ARG_INFO ()
83
+
45
84
/* {{{ geospatial_functions[]
46
85
*
47
86
* Every user visible function must have an entry in geospatial_functions[].
48
87
*/
49
88
const zend_function_entry geospatial_functions [] = {
50
89
PHP_FE (haversine , haversine_args )
51
90
PHP_FE (fraction_along_gc_line , fraction_along_gc_line_args )
91
+ PHP_FE (helmert , helmert_args )
92
+ PHP_FE (polar_to_cartesian , polar_to_cartesian_args )
93
+ PHP_FE (cartesian_to_polar , cartesian_to_polar_args )
94
+ PHP_FE (transform_datum , transform_datum_args )
95
+ PHP_FE (dms_to_decimal , dms_to_decimal_args )
96
+ PHP_FE (decimal_to_dms , decimal_to_dms_args )
97
+ /* End of functions */
52
98
{ NULL , NULL , NULL }
53
99
};
54
100
/* }}} */
@@ -83,6 +129,8 @@ PHP_MINIT_FUNCTION(geospatial)
83
129
{
84
130
REGISTER_DOUBLE_CONSTANT ("GEO_DEG_TO_RAD" , GEO_DEG_TO_RAD , CONST_CS | CONST_PERSISTENT );
85
131
REGISTER_DOUBLE_CONSTANT ("GEO_EARTH_RADIUS" , GEO_EARTH_RADIUS , CONST_CS | CONST_PERSISTENT );
132
+ REGISTER_LONG_CONSTANT ("GEO_AIRY_1830" , GEO_AIRY_1830 , CONST_CS | CONST_PERSISTENT );
133
+ REGISTER_LONG_CONSTANT ("GEO_WGS84" , GEO_WGS84 , CONST_CS | CONST_PERSISTENT );
86
134
return SUCCESS ;
87
135
}
88
136
/* }}} */
@@ -174,7 +222,252 @@ double php_geo_haversine(double from_lat, double from_long, double to_lat, doubl
174
222
return result ;
175
223
}
176
224
177
- /* {{{ proto double haversine(double from_lat, double from_long, double to_lat, double to_long [, double radius ])
225
+ geo_ellipsoid get_ellipsoid (long ellipsoid_const )
226
+ {
227
+ switch (ellipsoid_const ) {
228
+ case GEO_AIRY_1830 :
229
+ return airy_1830 ;
230
+ break ;
231
+ case GEO_WGS84 :
232
+ default :
233
+ return wgs84 ;
234
+ break ;
235
+ }
236
+ }
237
+
238
+ geo_helmert_constants get_helmert_constants (long from , long to )
239
+ {
240
+ switch (from - to ) {
241
+ case 1 :
242
+ return osgb36_wgs84 ;
243
+ break ;
244
+ default :
245
+ case -1 :
246
+ return wgs84_osgb36 ;
247
+ break ;
248
+ }
249
+ }
250
+
251
+ geo_cartesian helmert (double x , double y , double z , geo_helmert_constants helmet_consts )
252
+ {
253
+ double rX , rY , rZ ;
254
+ double xOut , yOut , zOut ;
255
+ double scale_change ;
256
+ geo_cartesian point ;
257
+ scale_change = 1 + (helmet_consts .scale_change );
258
+ rX = helmet_consts .rotation_x / GEO_SEC_IN_DEG * GEO_DEG_TO_RAD ;
259
+ rY = helmet_consts .rotation_y / GEO_SEC_IN_DEG * GEO_DEG_TO_RAD ;
260
+ rZ = helmet_consts .rotation_z / GEO_SEC_IN_DEG * GEO_DEG_TO_RAD ;
261
+
262
+ xOut = helmet_consts .translation_x + ((x - (rZ * y ) + (rY * z )) * scale_change );
263
+
264
+ yOut = helmet_consts .translation_y + (((rZ * x ) + y - (rX * z )) * scale_change );
265
+
266
+ zOut = helmet_consts .translation_z + (((-1 * rY * x ) + (rX * y ) + z ) * scale_change );
267
+
268
+ point .x = xOut ;
269
+ point .y = yOut ;
270
+ point .z = zOut ;
271
+ return point ;
272
+ }
273
+
274
+ geo_cartesian polar_to_cartesian (double latitude , double longitude , geo_ellipsoid eli )
275
+ {
276
+ double x , y , z ;
277
+
278
+ geo_cartesian point ;
279
+ double phi = latitude * GEO_DEG_TO_RAD ;
280
+ double lambda = longitude * GEO_DEG_TO_RAD ;
281
+ double eSq = ((eli .a * eli .a ) - (eli .b * eli .b )) / (eli .a * eli .a );
282
+ double nu = eli .a / sqrt (1 - (eSq * sin (phi ) * sin (phi )));
283
+ x = nu + HEIGHT ;
284
+ x *= cos (phi ) * cos (lambda );
285
+ y = nu + HEIGHT ;
286
+ y *= cos (phi ) * sin (lambda );
287
+ z = ((1 - eSq ) * nu ) + HEIGHT ;
288
+ z *= sin (phi );
289
+ point .x = x ;
290
+ point .y = y ;
291
+ point .z = z ;
292
+ return point ;
293
+ }
294
+
295
+
296
+ geo_lat_long cartesian_to_polar (double x , double y , double z , geo_ellipsoid eli )
297
+ {
298
+
299
+ double nu , lambda , h ;
300
+ geo_lat_long polar ;
301
+
302
+ /* aiming for 1m accuracy */
303
+ double precision = 0.1 / eli .a ;
304
+ double eSq = ((eli .a * eli .a ) - (eli .b * eli .b )) / (eli .a * eli .a );
305
+ double p = sqrt (x * x + y * y );
306
+ double phi = atan2 (z , p * (1 - eSq ));
307
+ double phiP = 2 * M_PI ;
308
+
309
+ while (abs (phi - phiP ) > precision ) {
310
+ nu = eli .a / sqrt (1 - eSq * sin (phi ) * sin (phi ));
311
+ phiP = phi ;
312
+ phi = atan2 (z + eSq * nu * sin (phi ), p );
313
+ }
314
+
315
+ lambda = atan2 (y ,x );
316
+ h = p / cos (phi ) - nu ;
317
+ polar .latitude = phi / GEO_DEG_TO_RAD ;
318
+ polar .longitude = lambda / GEO_DEG_TO_RAD ;
319
+ polar .height = h ;
320
+
321
+ return polar ;
322
+ }
323
+
324
+ /* {{{ proto dms_to_decimal(double degrees, double minutes, double seconds [,string direction])
325
+ * Convert degrees, minutes & seconds values to decimal degrees */
326
+ PHP_FUNCTION (dms_to_decimal )
327
+ {
328
+ double degrees , minutes , sign ;
329
+ double seconds , decimal ;
330
+ char * direction = "" ;
331
+ int direction_len ;
332
+
333
+ if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC , "ddd|s" , & degrees , & minutes , & seconds , & direction , & direction_len ) == FAILURE ) {
334
+ return ;
335
+ }
336
+
337
+ if (strcmp ("" , direction ) == 0 ) {
338
+ sign = degrees > 1 ? 1 : -1 ;
339
+ } else {
340
+ sign = strcmp (direction , "S" ) == 0 || strcmp (direction , "W" ) == 0 ? -1 : 1 ;
341
+ }
342
+
343
+ decimal = abs (degrees ) + minutes / 60 + seconds / 3600 ;
344
+ decimal *= sign ;
345
+ RETURN_DOUBLE (decimal );
346
+ }
347
+ /* }}} */
348
+
349
+ /* {{{ proto decimal_to_dms(double decimal, string coordinate)
350
+ * Convert decimal degrees value to whole degrees and minutes and decimal seconds */
351
+ PHP_FUNCTION (decimal_to_dms )
352
+ {
353
+ double decimal , seconds ;
354
+ int degrees , minutes ;
355
+ char * direction ;
356
+ char * coordinate ;
357
+ int coordinate_len ;
358
+
359
+ if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC , "ds" , & decimal , & coordinate , & coordinate_len ) == FAILURE ) {
360
+ return ;
361
+ }
362
+
363
+ if (strcmp (coordinate , "longitude" ) == 0 ) {
364
+ direction = decimal < 1 ? "W" : "E" ;
365
+ } else {
366
+ direction = decimal < 1 ? "S" : "N" ;
367
+ }
368
+
369
+ array_init (return_value );
370
+ decimal = fabs (decimal );
371
+ degrees = (int ) decimal ;
372
+ minutes = decimal * 60 - degrees * 60 ;
373
+ seconds = decimal * 3600 - degrees * 3600 - minutes * 60 ;
374
+ add_assoc_long (return_value , "degrees" , degrees );
375
+ add_assoc_long (return_value , "minutes" , minutes );
376
+ add_assoc_double (return_value , "seconds" , seconds );
377
+ add_assoc_string (return_value , "direction" , direction , 1 );
378
+ }
379
+ /* }}} */
380
+
381
+ /* {{{ proto helmert(double x, double y, double z [, long from_reference_ellipsoid, long to_reference_ellipsoid])
382
+ * Convert polar ones (latitude, longitude) tp cartesian co-ordiantes (x, y, z) */
383
+ PHP_FUNCTION (helmert )
384
+ {
385
+ double x , y , z ;
386
+ geo_cartesian point ;
387
+ long from_reference_ellipsoid , to_reference_ellipsoid ;
388
+ if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC , "ddd|ll" , & x , & y , & z , & from_reference_ellipsoid , & to_reference_ellipsoid ) == FAILURE ) {
389
+ return ;
390
+ }
391
+
392
+ array_init (return_value );
393
+ geo_helmert_constants helmert_constants = get_helmert_constants (from_reference_ellipsoid , to_reference_ellipsoid );
394
+ point = helmert (x , y , z , helmert_constants );
395
+ add_assoc_double (return_value , "x" , point .x );
396
+ add_assoc_double (return_value , "y" , point .y );
397
+ add_assoc_double (return_value , "z" , point .z );
398
+ }
399
+ /* }}} */
400
+
401
+ /* {{{ proto polar_to_cartesian(double latitude, double longitude[, long reference_ellipsoid])
402
+ * Convert polar ones (latitude, longitude) tp cartesian co-ordiantes (x, y, z) */
403
+ PHP_FUNCTION (polar_to_cartesian )
404
+ {
405
+ double latitude , longitude ;
406
+ long reference_ellipsoid ;
407
+ geo_cartesian point ;
408
+
409
+ if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC , "dd|l" , & latitude , & longitude , & reference_ellipsoid ) == FAILURE ) {
410
+ return ;
411
+ }
412
+
413
+ geo_ellipsoid eli = get_ellipsoid (reference_ellipsoid );
414
+ array_init (return_value );
415
+ point = polar_to_cartesian (latitude , longitude , eli );
416
+ add_assoc_double (return_value , "x" , point .x );
417
+ add_assoc_double (return_value , "y" , point .y );
418
+ add_assoc_double (return_value , "z" , point .z );
419
+ }
420
+ /* }}} */
421
+
422
+ /* {{{ proto cartesian_to_polar(double x, double y, double z [, long reference_ellipsoid])
423
+ * Convert cartesian co-ordiantes (x, y, z) to polar ones (latitude, longitude) */
424
+ PHP_FUNCTION (cartesian_to_polar )
425
+ {
426
+ double x , y , z ;
427
+ long reference_ellipsoid ;
428
+ geo_lat_long polar ;
429
+
430
+ if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC , "ddd|l" , & x , & y , & z , & reference_ellipsoid ) == FAILURE ) {
431
+ return ;
432
+ }
433
+
434
+ geo_ellipsoid eli = get_ellipsoid (reference_ellipsoid );
435
+ array_init (return_value );
436
+ polar = cartesian_to_polar (x , y , z , eli );
437
+ add_assoc_double (return_value , "lat" , polar .latitude );
438
+ add_assoc_double (return_value , "long" , polar .longitude );
439
+ add_assoc_double (return_value , "height" , polar .height );
440
+ }
441
+ /* }}} */
442
+
443
+
444
+ /* {{{ proto transform_datum(double latitude, double longitude, long from_reference_ellipsoid, long to_reference_ellipsoid)
445
+ * Unified function to transform projection of geo-cordinates between datums */
446
+ PHP_FUNCTION (transform_datum )
447
+ {
448
+ double latitude , longitude ;
449
+ long from_reference_ellipsoid , to_reference_ellipsoid ;
450
+ geo_cartesian point , converted_point ;
451
+ geo_lat_long polar ;
452
+ if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC , "ddll" , & latitude , & longitude , & from_reference_ellipsoid , & to_reference_ellipsoid ) == FAILURE ) {
453
+ return ;
454
+ }
455
+
456
+ geo_ellipsoid eli_from = get_ellipsoid (from_reference_ellipsoid );
457
+ geo_ellipsoid eli_to = get_ellipsoid (to_reference_ellipsoid );
458
+ point = polar_to_cartesian (latitude , longitude , eli_from );
459
+ geo_helmert_constants helmert_constants = get_helmert_constants (from_reference_ellipsoid , to_reference_ellipsoid );
460
+ converted_point = helmert (point .x , point .y , point .z , helmert_constants );
461
+ polar = cartesian_to_polar (converted_point .x , converted_point .y , converted_point .z , eli_to );
462
+
463
+ array_init (return_value );
464
+ add_assoc_double (return_value , "lat" , polar .latitude );
465
+ add_assoc_double (return_value , "long" , polar .longitude );
466
+ add_assoc_double (return_value , "height" , polar .height );
467
+ }
468
+ /* }}} */
469
+
470
+ /* {{{ proto haversine(double fromLat, double fromLong, double toLat, double toLong [, double radius ])
178
471
* Calculates the greater circle distance between the two lattitude/longitude pairs */
179
472
PHP_FUNCTION (haversine )
180
473
{
0 commit comments