Skip to content

Commit 53918a0

Browse files
committed
Merge pull request #5 from derickr/rdp
Added a Ramer-Douglas-Peucker line/polygon simplification algorithm.
2 parents b7f4292 + 01d68a7 commit 53918a0

File tree

5 files changed

+179
-54
lines changed

5 files changed

+179
-54
lines changed

config.m4

Lines changed: 1 addition & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,9 @@
11
dnl $Id$
22
dnl config.m4 for extension geospatial
33

4-
dnl Comments in this file start with the string 'dnl'.
5-
dnl Remove where necessary. This file will not work
6-
dnl without editing.
7-
8-
dnl If your extension references something external, use with:
9-
10-
dnl PHP_ARG_WITH(geospatial, for geospatial support,
11-
dnl Make sure that the comment is aligned:
12-
dnl [ --with-geospatial Include geospatial support])
13-
14-
dnl Otherwise use enable:
15-
164
PHP_ARG_ENABLE(geospatial, whether to enable geospatial support,
175
[ --enable-geospatial Enable geospatial support])
186

197
if test "$PHP_GEOSPATIAL" != "no"; then
20-
dnl Write more examples of tests here...
21-
22-
dnl # --with-geospatial -> check with-path
23-
dnl SEARCH_PATH="/usr/local /usr" # you might want to change this
24-
dnl SEARCH_FOR="/include/geospatial.h" # you most likely want to change this
25-
dnl if test -r $PHP_GEOSPATIAL/$SEARCH_FOR; then # path given as parameter
26-
dnl GEOSPATIAL_DIR=$PHP_GEOSPATIAL
27-
dnl else # search default path list
28-
dnl AC_MSG_CHECKING([for geospatial files in default path])
29-
dnl for i in $SEARCH_PATH ; do
30-
dnl if test -r $i/$SEARCH_FOR; then
31-
dnl GEOSPATIAL_DIR=$i
32-
dnl AC_MSG_RESULT(found in $i)
33-
dnl fi
34-
dnl done
35-
dnl fi
36-
dnl
37-
dnl if test -z "$GEOSPATIAL_DIR"; then
38-
dnl AC_MSG_RESULT([not found])
39-
dnl AC_MSG_ERROR([Please reinstall the geospatial distribution])
40-
dnl fi
41-
42-
dnl # --with-geospatial -> add include path
43-
dnl PHP_ADD_INCLUDE($GEOSPATIAL_DIR/include)
44-
45-
dnl # --with-geospatial -> check for lib and symbol presence
46-
dnl LIBNAME=geospatial # you may want to change this
47-
dnl LIBSYMBOL=geospatial # you most likely want to change this
48-
49-
dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
50-
dnl [
51-
dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $GEOSPATIAL_DIR/lib, GEOSPATIAL_SHARED_LIBADD)
52-
dnl AC_DEFINE(HAVE_GEOSPATIALLIB,1,[ ])
53-
dnl ],[
54-
dnl AC_MSG_ERROR([wrong geospatial lib version or lib not found])
55-
dnl ],[
56-
dnl -L$GEOSPATIAL_DIR/lib -lm
57-
dnl ])
58-
dnl
59-
dnl PHP_SUBST(GEOSPATIAL_SHARED_LIBADD)
60-
61-
PHP_NEW_EXTENSION(geospatial, geospatial.c, $ext_shared)
8+
PHP_NEW_EXTENSION(geospatial, geospatial.c geo_array.c, $ext_shared)
629
fi

geo_array.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include <stdlib.h>
2+
3+
#include "geo_array.h"
4+
5+
geo_array *geo_array_ctor(int element_count)
6+
{
7+
geo_array *tmp;
8+
9+
tmp = malloc(sizeof(geo_array));
10+
tmp->count = element_count;
11+
tmp->status = calloc(1, element_count);
12+
tmp->x = (double*) calloc(1, element_count * sizeof(double));
13+
tmp->y = (double*) calloc(1, element_count * sizeof(double));
14+
15+
return tmp;
16+
}
17+
18+
void geo_array_dtor(geo_array *points)
19+
{
20+
free(points->status);
21+
free(points->x);
22+
free(points->y);
23+
free(points);
24+
}

geo_array.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
typedef struct geo_array {
2+
double *x;
3+
double *y;
4+
char *status;
5+
int count;
6+
} geo_array;
7+
8+
geo_array *geo_array_ctor(int element_count);
9+
void geo_array_dtor(geo_array *points);

geospatial.c

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "php_ini.h"
2828
#include "ext/standard/info.h"
2929
#include "php_geospatial.h"
30+
#include "geo_array.h"
3031

3132
ZEND_BEGIN_ARG_INFO_EX(haversine_args, 0, 0, 4)
3233
ZEND_ARG_INFO(0, fromLatitude)
@@ -81,6 +82,11 @@ ZEND_BEGIN_ARG_INFO_EX(decimal_to_dms_args, 0, 0, 2)
8182
ZEND_ARG_INFO(0, coordinate)
8283
ZEND_END_ARG_INFO()
8384

85+
ZEND_BEGIN_ARG_INFO_EX(rdp_simplify_args, 0, 0, 2)
86+
ZEND_ARG_INFO(0, pointsArray)
87+
ZEND_ARG_INFO(0, epsilon)
88+
ZEND_END_ARG_INFO()
89+
8490
/* {{{ geospatial_functions[]
8591
*
8692
* Every user visible function must have an entry in geospatial_functions[].
@@ -94,6 +100,7 @@ const zend_function_entry geospatial_functions[] = {
94100
PHP_FE(transform_datum, transform_datum_args)
95101
PHP_FE(dms_to_decimal, dms_to_decimal_args)
96102
PHP_FE(decimal_to_dms, decimal_to_dms_args)
103+
PHP_FE(rdp_simplify, rdp_simplify_args)
97104
/* End of functions */
98105
{ NULL, NULL, NULL }
99106
};
@@ -449,6 +456,7 @@ PHP_FUNCTION(transform_datum)
449456
long from_reference_ellipsoid, to_reference_ellipsoid;
450457
geo_cartesian point, converted_point;
451458
geo_lat_long polar;
459+
452460
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ddll", &latitude, &longitude, &from_reference_ellipsoid, &to_reference_ellipsoid) == FAILURE) {
453461
return;
454462
}
@@ -529,6 +537,142 @@ PHP_FUNCTION(fraction_along_gc_line)
529537
}
530538
/* }}} */
531539

540+
geo_array *geo_hashtable_to_array(zval *array)
541+
{
542+
geo_array *tmp;
543+
int element_count;
544+
HashPosition pos;
545+
zval **entry;
546+
zval **z_lon, **z_lat;
547+
int i = 0;
548+
549+
element_count = zend_hash_num_elements(Z_ARRVAL_P(array));
550+
tmp = geo_array_ctor(element_count);
551+
552+
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
553+
while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&entry, &pos) == SUCCESS) {
554+
555+
if (Z_TYPE_PP(entry) != IS_ARRAY) {
556+
goto failure;
557+
}
558+
if (zend_hash_num_elements(Z_ARRVAL_PP(entry)) != 2)
559+
{
560+
goto failure;
561+
}
562+
if (zend_hash_index_find(HASH_OF(*entry), 0, (void**) &z_lon) != SUCCESS) {
563+
return 0;
564+
}
565+
if (zend_hash_index_find(HASH_OF(*entry), 1, (void**) &z_lat) != SUCCESS) {
566+
return 0;
567+
}
568+
convert_to_double_ex(z_lon);
569+
convert_to_double_ex(z_lat);
570+
571+
tmp->x[i] = Z_DVAL_PP(z_lon);
572+
tmp->y[i] = Z_DVAL_PP(z_lat);
573+
tmp->status[i] = 1;
574+
575+
zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos);
576+
i++;
577+
}
578+
579+
return tmp;
580+
581+
failure:
582+
geo_array_dtor(tmp);
583+
return NULL;
584+
}
585+
586+
double rdp_find_perpendicular_distable(double pX, double pY, double p1X, double p1Y, double p2X, double p2Y)
587+
{
588+
double slope, intercept, result;
589+
590+
if (p1X == p2X) {
591+
return fabs(pX - p1X);
592+
} else {
593+
slope = (p2Y - p1Y) / (p2X - p1X);
594+
intercept = p1Y - (slope * p1X);
595+
result = fabs(slope * pX - pY + intercept) / sqrt(pow(slope, 2) + 1);
596+
return result;
597+
}
598+
}
599+
600+
void rdp_simplify(geo_array *points, double epsilon, int start, int end)
601+
{
602+
double firstX = points->x[start];
603+
double firstY = points->y[start];
604+
double lastX = points->x[end];
605+
double lastY = points->y[end];
606+
int index = -1;
607+
double dist = 0.0, current_dist;
608+
int i;
609+
610+
if (end - start < 2) {
611+
return;
612+
}
613+
614+
for (i = start + 1; i < end; i++) {
615+
if (!points->status[i]) {
616+
continue;
617+
}
618+
619+
current_dist = rdp_find_perpendicular_distable(points->x[i], points->y[i], firstX, firstY, lastX, lastY);
620+
621+
if (current_dist > dist) {
622+
dist = current_dist;
623+
index = i;
624+
}
625+
}
626+
627+
if (dist > epsilon) {
628+
rdp_simplify(points, epsilon, start, index);
629+
rdp_simplify(points, epsilon, index, end);
630+
631+
return;
632+
} else {
633+
for (i = start + 1; i < end; i++) {
634+
points->status[i] = 0;
635+
}
636+
return;
637+
}
638+
}
639+
640+
/* {{{ proto array rdp_simplify(array points, float epsilon)
641+
Simplifies a 2D dimensional line according to the Ramer-Douglas-Peucker algorithm */
642+
PHP_FUNCTION(rdp_simplify)
643+
{
644+
zval *points_array;
645+
double epsilon;
646+
geo_array *points;
647+
int i;
648+
zval *pair;
649+
650+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zd", &points_array, &epsilon) == FAILURE) {
651+
return;
652+
}
653+
654+
if (!Z_TYPE_P(points_array) == IS_ARRAY) {
655+
return;
656+
}
657+
658+
array_init(return_value);
659+
660+
points = geo_hashtable_to_array(points_array);
661+
rdp_simplify(points, epsilon, 0, points->count - 1);
662+
for (i = 0; i < points->count; i++) {
663+
if (points->status[i]) {
664+
MAKE_STD_ZVAL(pair);
665+
array_init(pair);
666+
add_next_index_double(pair, points->x[i]);
667+
add_next_index_double(pair, points->y[i]);
668+
add_next_index_zval(return_value, pair);
669+
}
670+
}
671+
672+
geo_array_dtor(points);
673+
}
674+
/* }}} */
675+
532676
/*
533677
* Local variables:
534678
* tab-width: 4

php_geospatial.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ PHP_FUNCTION(cartesian_to_polar);
128128
PHP_FUNCTION(transform_datum);
129129
PHP_FUNCTION(dms_to_decimal);
130130
PHP_FUNCTION(decimal_to_dms);
131+
PHP_FUNCTION(rdp_simplify);
131132

132133
#endif /* PHP_GEOSPATIAL_H */
133134

0 commit comments

Comments
 (0)