Skip to content

Added a Ramer-Douglas-Peucker line/polygon simplification algorithm. #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 17, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 1 addition & 54 deletions config.m4
Original file line number Diff line number Diff line change
@@ -1,62 +1,9 @@
dnl $Id$
dnl config.m4 for extension geospatial

dnl Comments in this file start with the string 'dnl'.
dnl Remove where necessary. This file will not work
dnl without editing.

dnl If your extension references something external, use with:

dnl PHP_ARG_WITH(geospatial, for geospatial support,
dnl Make sure that the comment is aligned:
dnl [ --with-geospatial Include geospatial support])

dnl Otherwise use enable:

PHP_ARG_ENABLE(geospatial, whether to enable geospatial support,
[ --enable-geospatial Enable geospatial support])

if test "$PHP_GEOSPATIAL" != "no"; then
dnl Write more examples of tests here...

dnl # --with-geospatial -> check with-path
dnl SEARCH_PATH="/usr/local /usr" # you might want to change this
dnl SEARCH_FOR="/include/geospatial.h" # you most likely want to change this
dnl if test -r $PHP_GEOSPATIAL/$SEARCH_FOR; then # path given as parameter
dnl GEOSPATIAL_DIR=$PHP_GEOSPATIAL
dnl else # search default path list
dnl AC_MSG_CHECKING([for geospatial files in default path])
dnl for i in $SEARCH_PATH ; do
dnl if test -r $i/$SEARCH_FOR; then
dnl GEOSPATIAL_DIR=$i
dnl AC_MSG_RESULT(found in $i)
dnl fi
dnl done
dnl fi
dnl
dnl if test -z "$GEOSPATIAL_DIR"; then
dnl AC_MSG_RESULT([not found])
dnl AC_MSG_ERROR([Please reinstall the geospatial distribution])
dnl fi

dnl # --with-geospatial -> add include path
dnl PHP_ADD_INCLUDE($GEOSPATIAL_DIR/include)

dnl # --with-geospatial -> check for lib and symbol presence
dnl LIBNAME=geospatial # you may want to change this
dnl LIBSYMBOL=geospatial # you most likely want to change this

dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
dnl [
dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $GEOSPATIAL_DIR/lib, GEOSPATIAL_SHARED_LIBADD)
dnl AC_DEFINE(HAVE_GEOSPATIALLIB,1,[ ])
dnl ],[
dnl AC_MSG_ERROR([wrong geospatial lib version or lib not found])
dnl ],[
dnl -L$GEOSPATIAL_DIR/lib -lm
dnl ])
dnl
dnl PHP_SUBST(GEOSPATIAL_SHARED_LIBADD)

PHP_NEW_EXTENSION(geospatial, geospatial.c, $ext_shared)
PHP_NEW_EXTENSION(geospatial, geospatial.c geo_array.c, $ext_shared)
fi
24 changes: 24 additions & 0 deletions geo_array.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include <stdlib.h>

#include "geo_array.h"

geo_array *geo_array_ctor(int element_count)
{
geo_array *tmp;

tmp = malloc(sizeof(geo_array));
tmp->count = element_count;
tmp->status = calloc(1, element_count);
tmp->x = (double*) calloc(1, element_count * sizeof(double));
tmp->y = (double*) calloc(1, element_count * sizeof(double));

return tmp;
}

void geo_array_dtor(geo_array *points)
{
free(points->status);
free(points->x);
free(points->y);
free(points);
}
9 changes: 9 additions & 0 deletions geo_array.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
typedef struct geo_array {
double *x;
double *y;
char *status;
int count;
} geo_array;

geo_array *geo_array_ctor(int element_count);
void geo_array_dtor(geo_array *points);
144 changes: 144 additions & 0 deletions geospatial.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_geospatial.h"
#include "geo_array.h"

ZEND_BEGIN_ARG_INFO_EX(haversine_args, 0, 0, 4)
ZEND_ARG_INFO(0, fromLatitude)
Expand Down Expand Up @@ -81,6 +82,11 @@ ZEND_BEGIN_ARG_INFO_EX(decimal_to_dms_args, 0, 0, 2)
ZEND_ARG_INFO(0, coordinate)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(rdp_simplify_args, 0, 0, 2)
ZEND_ARG_INFO(0, pointsArray)
ZEND_ARG_INFO(0, epsilon)
ZEND_END_ARG_INFO()

/* {{{ geospatial_functions[]
*
* Every user visible function must have an entry in geospatial_functions[].
Expand All @@ -94,6 +100,7 @@ const zend_function_entry geospatial_functions[] = {
PHP_FE(transform_datum, transform_datum_args)
PHP_FE(dms_to_decimal, dms_to_decimal_args)
PHP_FE(decimal_to_dms, decimal_to_dms_args)
PHP_FE(rdp_simplify, rdp_simplify_args)
/* End of functions */
{ NULL, NULL, NULL }
};
Expand Down Expand Up @@ -449,6 +456,7 @@ PHP_FUNCTION(transform_datum)
long from_reference_ellipsoid, to_reference_ellipsoid;
geo_cartesian point, converted_point;
geo_lat_long polar;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ddll", &latitude, &longitude, &from_reference_ellipsoid, &to_reference_ellipsoid) == FAILURE) {
return;
}
Expand Down Expand Up @@ -529,6 +537,142 @@ PHP_FUNCTION(fraction_along_gc_line)
}
/* }}} */

geo_array *geo_hashtable_to_array(zval *array)
{
geo_array *tmp;
int element_count;
HashPosition pos;
zval **entry;
zval **z_lon, **z_lat;
int i = 0;

element_count = zend_hash_num_elements(Z_ARRVAL_P(array));
tmp = geo_array_ctor(element_count);

zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&entry, &pos) == SUCCESS) {

if (Z_TYPE_PP(entry) != IS_ARRAY) {
goto failure;
}
if (zend_hash_num_elements(Z_ARRVAL_PP(entry)) != 2)
{
goto failure;
}
if (zend_hash_index_find(HASH_OF(*entry), 0, (void**) &z_lon) != SUCCESS) {
return 0;
}
if (zend_hash_index_find(HASH_OF(*entry), 1, (void**) &z_lat) != SUCCESS) {
return 0;
}
convert_to_double_ex(z_lon);
convert_to_double_ex(z_lat);

tmp->x[i] = Z_DVAL_PP(z_lon);
tmp->y[i] = Z_DVAL_PP(z_lat);
tmp->status[i] = 1;

zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos);
i++;
}

return tmp;

failure:
geo_array_dtor(tmp);
return NULL;
}

double rdp_find_perpendicular_distable(double pX, double pY, double p1X, double p1Y, double p2X, double p2Y)
{
double slope, intercept, result;

if (p1X == p2X) {
return fabs(pX - p1X);
} else {
slope = (p2Y - p1Y) / (p2X - p1X);
intercept = p1Y - (slope * p1X);
result = fabs(slope * pX - pY + intercept) / sqrt(pow(slope, 2) + 1);
return result;
}
}

void rdp_simplify(geo_array *points, double epsilon, int start, int end)
{
double firstX = points->x[start];
double firstY = points->y[start];
double lastX = points->x[end];
double lastY = points->y[end];
int index = -1;
double dist = 0.0, current_dist;
int i;

if (end - start < 2) {
return;
}

for (i = start + 1; i < end; i++) {
if (!points->status[i]) {
continue;
}

current_dist = rdp_find_perpendicular_distable(points->x[i], points->y[i], firstX, firstY, lastX, lastY);

if (current_dist > dist) {
dist = current_dist;
index = i;
}
}

if (dist > epsilon) {
rdp_simplify(points, epsilon, start, index);
rdp_simplify(points, epsilon, index, end);

return;
} else {
for (i = start + 1; i < end; i++) {
points->status[i] = 0;
}
return;
}
}

/* {{{ proto array rdp_simplify(array points, float epsilon)
Simplifies a 2D dimensional line according to the Ramer-Douglas-Peucker algorithm */
PHP_FUNCTION(rdp_simplify)
{
zval *points_array;
double epsilon;
geo_array *points;
int i;
zval *pair;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zd", &points_array, &epsilon) == FAILURE) {
return;
}

if (!Z_TYPE_P(points_array) == IS_ARRAY) {
return;
}

array_init(return_value);

points = geo_hashtable_to_array(points_array);
rdp_simplify(points, epsilon, 0, points->count - 1);
for (i = 0; i < points->count; i++) {
if (points->status[i]) {
MAKE_STD_ZVAL(pair);
array_init(pair);
add_next_index_double(pair, points->x[i]);
add_next_index_double(pair, points->y[i]);
add_next_index_zval(return_value, pair);
}
}

geo_array_dtor(points);
}
/* }}} */

/*
* Local variables:
* tab-width: 4
Expand Down
1 change: 1 addition & 0 deletions php_geospatial.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ PHP_FUNCTION(cartesian_to_polar);
PHP_FUNCTION(transform_datum);
PHP_FUNCTION(dms_to_decimal);
PHP_FUNCTION(decimal_to_dms);
PHP_FUNCTION(rdp_simplify);

#endif /* PHP_GEOSPATIAL_H */

Expand Down