@@ -35,12 +35,20 @@ ZEND_BEGIN_ARG_INFO_EX(haversine_args, 0, 0, 4)
35
35
ZEND_ARG_INFO (0 , radius )
36
36
ZEND_END_ARG_INFO ()
37
37
38
+ ZEND_BEGIN_ARG_INFO_EX (fraction_along_gc_line_args , 0 , 0 , 3 )
39
+ ZEND_ARG_INFO (0 , geoJsonPoint1 )
40
+ ZEND_ARG_INFO (0 , geoJsonPoint2 )
41
+ ZEND_ARG_INFO (0 , fraction )
42
+ ZEND_ARG_INFO (0 , radius )
43
+ ZEND_END_ARG_INFO ()
44
+
38
45
/* {{{ geospatial_functions[]
39
46
*
40
47
* Every user visible function must have an entry in geospatial_functions[].
41
48
*/
42
49
const zend_function_entry geospatial_functions [] = {
43
50
PHP_FE (haversine , haversine_args )
51
+ PHP_FE (fraction_along_gc_line , fraction_along_gc_line_args )
44
52
{ NULL , NULL , NULL }
45
53
};
46
54
/* }}} */
@@ -97,6 +105,56 @@ PHP_MINFO_FUNCTION(geospatial)
97
105
}
98
106
/* }}} */
99
107
108
+ /* {{{ Helpers */
109
+ void retval_point_from_coordinates (zval * return_value , double lon , double lat )
110
+ {
111
+ zval * coordinates ;
112
+
113
+ array_init (return_value );
114
+ MAKE_STD_ZVAL (coordinates );
115
+ array_init (coordinates );
116
+ add_assoc_string_ex (return_value , "type" , sizeof ("type" ), "Point" , 1 );
117
+ add_next_index_double (coordinates , lon );
118
+ add_next_index_double (coordinates , lat );
119
+ add_assoc_zval_ex (return_value , "coordinates" , sizeof ("coordinates" ), coordinates );
120
+ }
121
+
122
+ int geojson_point_to_lon_lat (zval * point , double * lon , double * lat )
123
+ {
124
+ zval * * type , * * coordinates , * * z_lon , * * z_lat ;
125
+ HashTable * coords ;
126
+
127
+ if (zend_hash_find (HASH_OF (point ), "type" , sizeof ("type" ), (void * * ) & type ) != SUCCESS ) {
128
+ return 0 ;
129
+ }
130
+ if (Z_TYPE_PP (type ) != IS_STRING || strcmp (Z_STRVAL_PP (type ), "Point" ) != 0 ) {
131
+ return 0 ;
132
+ }
133
+ if (zend_hash_find (HASH_OF (point ), "coordinates" , sizeof ("coordinates" ), (void * * ) & coordinates ) != SUCCESS ) {
134
+ return 0 ;
135
+ }
136
+ if (Z_TYPE_PP (coordinates ) != IS_ARRAY ) {
137
+ return 0 ;
138
+ }
139
+ coords = HASH_OF (* coordinates );
140
+ if (coords -> nNumOfElements != 2 ) {
141
+ return 0 ;
142
+ }
143
+ if (zend_hash_index_find (coords , 0 , (void * * ) & z_lon ) != SUCCESS ) {
144
+ return 0 ;
145
+ }
146
+ if (zend_hash_index_find (coords , 1 , (void * * ) & z_lat ) != SUCCESS ) {
147
+ return 0 ;
148
+ }
149
+ convert_to_double_ex (z_lon );
150
+ convert_to_double_ex (z_lat );
151
+ * lon = Z_DVAL_PP (z_lon );
152
+ * lat = Z_DVAL_PP (z_lat );
153
+ return 1 ;
154
+ }
155
+
156
+ /* }}} */
157
+
100
158
double php_geo_haversine (double from_lat , double from_long , double to_lat , double to_long )
101
159
{
102
160
double delta_lat , delta_long ;
@@ -131,6 +189,53 @@ PHP_FUNCTION(haversine)
131
189
}
132
190
/* }}} */
133
191
192
+ void php_geo_fraction_along_gc_line (double from_lat , double from_long , double to_lat , double to_long , double fraction , double radius , double * res_lat , double * res_long )
193
+ {
194
+ double distance ;
195
+ double a , b , x , y , z ;
196
+
197
+ /* First we calculate the distance */
198
+ distance = php_geo_haversine (from_lat , from_long , to_lat , to_long );
199
+
200
+ a = sin ((1 - fraction ) * distance ) / sin (distance );
201
+ b = sin (fraction * distance ) / sin (distance );
202
+ x = a * cos (from_lat ) * cos (from_long ) + b * cos (to_lat ) * cos (to_long );
203
+ y = a * cos (from_lat ) * sin (from_long ) + b * cos (to_lat ) * sin (to_long );
204
+ z = a * sin (from_lat ) + b * sin (to_lat );
205
+
206
+ * res_lat = atan2 (z , sqrt (x * x + y * y ));
207
+ * res_long = atan2 (y , x );
208
+ }
209
+
210
+ /* {{{ proto GeoJSON fraction_along_gc_line(GeoJSONPoint from, GeoJSONPoint to, double fraction [, double radius ])
211
+ * Calculates a lat/long pair at a fraction (0-1) of the distance along a GC line */
212
+ PHP_FUNCTION (fraction_along_gc_line )
213
+ {
214
+ zval * from_geojson , * to_geojson ;
215
+ double from_lat , from_long , to_lat , to_long , fraction ;
216
+ double radius = GEO_EARTH_RADIUS ;
217
+ double res_lat , res_long ;
218
+
219
+ if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC , "aad|d" , & from_geojson , & to_geojson , & fraction , & radius ) == FAILURE ) {
220
+ return ;
221
+ }
222
+
223
+ geojson_point_to_lon_lat (from_geojson , & from_long , & from_lat );
224
+ geojson_point_to_lon_lat (to_geojson , & to_long , & to_lat );
225
+
226
+ php_geo_fraction_along_gc_line (
227
+ from_lat * GEO_DEG_TO_RAD ,
228
+ from_long * GEO_DEG_TO_RAD ,
229
+ to_lat * GEO_DEG_TO_RAD ,
230
+ to_long * GEO_DEG_TO_RAD ,
231
+ fraction , radius ,
232
+ & res_lat , & res_long
233
+ );
234
+
235
+ retval_point_from_coordinates (return_value , res_long / GEO_DEG_TO_RAD , res_lat / GEO_DEG_TO_RAD );
236
+ }
237
+ /* }}} */
238
+
134
239
/*
135
240
* Local variables:
136
241
* tab-width: 4
0 commit comments