Skip to content

Commit b7f4292

Browse files
committed
Merge branch 'master' of github.com:php-geospatial/geospatial
Conflicts: geospatial.c php_geospatial.h
2 parents 5ce87c6 + 191d165 commit b7f4292

11 files changed

+580
-1
lines changed

geospatial.c

Lines changed: 294 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
+----------------------------------------------------------------------+
1717
*/
1818

19+
1920
/* $Id$ */
2021

2122
#ifdef HAVE_CONFIG_H
@@ -42,13 +43,58 @@ ZEND_BEGIN_ARG_INFO_EX(fraction_along_gc_line_args, 0, 0, 3)
4243
ZEND_ARG_INFO(0, radius)
4344
ZEND_END_ARG_INFO()
4445

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+
4584
/* {{{ geospatial_functions[]
4685
*
4786
* Every user visible function must have an entry in geospatial_functions[].
4887
*/
4988
const zend_function_entry geospatial_functions[] = {
5089
PHP_FE(haversine, haversine_args)
5190
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 */
5298
{ NULL, NULL, NULL }
5399
};
54100
/* }}} */
@@ -83,6 +129,8 @@ PHP_MINIT_FUNCTION(geospatial)
83129
{
84130
REGISTER_DOUBLE_CONSTANT("GEO_DEG_TO_RAD", GEO_DEG_TO_RAD, CONST_CS | CONST_PERSISTENT);
85131
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);
86134
return SUCCESS;
87135
}
88136
/* }}} */
@@ -174,7 +222,252 @@ double php_geo_haversine(double from_lat, double from_long, double to_lat, doubl
174222
return result;
175223
}
176224

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 ])
178471
* Calculates the greater circle distance between the two lattitude/longitude pairs */
179472
PHP_FUNCTION(haversine)
180473
{

0 commit comments

Comments
 (0)