Skip to content

Commit

Permalink
Merged pull request php-geospatial#20
Browse files Browse the repository at this point in the history
  • Loading branch information
derickr committed Dec 29, 2017
2 parents c03c43a + 5fbedfd commit fa1844b
Show file tree
Hide file tree
Showing 11 changed files with 533 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ env:
before_script:
- phpize
- EXTRA_LDFLAGS="-precious-files-regex .libs/geospatial.gcno" LDFLAGS="-lgcov" CFLAGS="-Wall -ggdb3 -fno-strict-aliasing -coverage -O0" ./configure --enable-geospatial
- make -j 5 test && if ls tests/*.diff >/dev/null 2>&1; then echo "Tests failed" && exit 1; fi
- make -j 5 test && if ls tests/*.diff >/dev/null 2>&1; then echo "Tests failed" && cat tests/*.diff && exit 1; fi
- gcov --object-directory .libs *.c
- bash <(curl -s https://codecov.io/bash)

Expand Down
29 changes: 29 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,32 @@ you would use::
$point2 = [ 'type' => 'Point', 'coordinates' => [ 15, 10 ] ];

var_dump(fraction_along_gc_line($point1, $point2, 0.25));

Geohashing
----------

The `geohash_encode` function can be used to convert GeoJSON Point to a
geohash of a specific length (in this case, 12)::

$point = [ 'type' => 'Point', 'coordinates' => [ 16.4, 48.2 ] ];
echo geohash_encode( $point, 12 );

Which outputs::
u2edjnw17enr

Similarly, a hashed coordinates pair can be decoded using `geohash_decode`
function::

var_dump(geohash_decode('u2edjnw17enr'));
array(2) {
["type"]=>
string(5) "Point"
["coordinates"]=>
array(2) {
[0]=>
float(16.40000006184)
[1]=>
float(48.199999993667)
}
}
2 changes: 1 addition & 1 deletion config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ PHP_ARG_ENABLE(geospatial, whether to enable geospatial support,
[ --enable-geospatial Enable geospatial support])

if test "$PHP_GEOSPATIAL" != "no"; then
PHP_NEW_EXTENSION(geospatial, geospatial.c geo_array.c, $ext_shared)
PHP_NEW_EXTENSION(geospatial, geospatial.c geo_array.c geohash.c, $ext_shared)
fi
3 changes: 3 additions & 0 deletions geo_array.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
| Marcus Deglos <[email protected]> |
+----------------------------------------------------------------------+
*/
#ifndef PHP_GEO_ARRAY_H
#define PHP_GEO_ARRAY_H

typedef struct geo_array {
double *x;
Expand All @@ -30,3 +32,4 @@ typedef struct geo_array {
geo_array *geo_array_ctor(int element_count);
void geo_array_add(geo_array *points, double lat, double lon);
void geo_array_dtor(geo_array *points);
#endif /* PHP_GEO_ARRAY_H */
27 changes: 27 additions & 0 deletions geo_lat_long.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
+----------------------------------------------------------------------+
| PHP Version 7 |
+----------------------------------------------------------------------+
| Copyright (c) 2017 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| [email protected] so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Emir Beganovic <[email protected]> |
+----------------------------------------------------------------------+
*/
#ifndef PHP_GEO_LAT_LONG_H
#define PHP_GEO_LAT_LONG_H

typedef struct {
double x;
double y;
double z;
} geo_lat_long;

#endif
140 changes: 140 additions & 0 deletions geohash.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
+----------------------------------------------------------------------+
| PHP Version 7 |
+----------------------------------------------------------------------+
| Copyright (c) 2017 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| [email protected] so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Emir Beganovic <[email protected]> |
+----------------------------------------------------------------------+
*/

#include <stdlib.h>

#include "php.h"
#include "geo_lat_long.h"
#include "geohash.h"

#define MAX_LAT 90.0
#define MIN_LAT -90.0

#define MAX_LONG 180.0
#define MIN_LONG -180.0

typedef struct interval_string {
double high;
double low;
} interval_struct;

static char char_map[32] = "0123456789bcdefghjkmnpqrstuvwxyz";
static size_t char_map_size = sizeof(char_map);

char *php_geo_geohash_encode(double latitude, double longitude, int precision)
{
char *hash;
int steps;
double coord, mid;
int is_even = 1;
unsigned int hash_char = 0;
int i;
interval_struct lat_interval = { MAX_LAT, MIN_LAT };
interval_struct lng_interval = { MAX_LONG, MIN_LONG };
interval_struct *interval;

hash = (char*)safe_emalloc(precision, sizeof(char), 1);

hash[precision] = '\0';
steps = precision * 5.0;

for (i = 1; i <= steps; i++) {
if (is_even) {
interval = &lng_interval;
coord = longitude;
} else {
interval = &lat_interval;
coord = latitude;
}

mid = (interval->low + interval->high) / 2.0;
hash_char = hash_char << 1;

if (coord > mid) {
interval->low = mid;
hash_char |= 0x01;
} else {
interval->high = mid;
}

if (!(i % 5)) {
hash[(i - 1) / 5] = char_map[hash_char];
hash_char = 0;
}

is_even = !is_even;
}

return hash;
}

static unsigned int index_for_char(char c, char *string, size_t string_amount)
{
unsigned int index = -1;
int i;

for (i = 0; i < string_amount; i++) {
if (c == string[i]) {
index = i;
break;
}
}

return index;
}

geo_lat_long php_geo_geohash_decode(char *hash, size_t char_amount)
{
geo_lat_long coordinate;

if (char_amount) {
int charmap_index;
double delta;
int i, j;
interval_struct lat_interval = { MAX_LAT, MIN_LAT };
interval_struct lng_interval = { MAX_LONG, MIN_LONG };
interval_struct *interval;

int is_even = 1;

for (i = 0; i < char_amount; i++) {
charmap_index = index_for_char(hash[i], char_map, char_map_size);

/* Interpret the last 5 bits of the integer */
for (j = 0; j < 5; j++) {
interval = is_even ? &lng_interval : &lat_interval;

delta = (interval->high - interval->low) / 2.0;

if ((charmap_index << j) & 0x0010) {
interval->low += delta;
} else {
interval->high -= delta;
}

is_even = !is_even;
}
}

coordinate.x = lat_interval.high - ((lat_interval.high - lat_interval.low) / 2.0);
coordinate.y = lng_interval.high - ((lng_interval.high - lng_interval.low) / 2.0);
coordinate.z = 0;
}

return coordinate;
}
24 changes: 24 additions & 0 deletions geohash.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5/7 |
+----------------------------------------------------------------------+
| Copyright (c) 2017 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| [email protected] so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Emir Beganovic <[email protected]> |
+----------------------------------------------------------------------+
*/
#ifndef PHP_GEOHASH_H
#define PHP_GEOHASH_H

char *php_geo_geohash_encode(double lat, double lng, int precision);
geo_lat_long php_geo_geohash_decode(char *hash, size_t hash_len);

#endif /* PHP_GEOHASH_H */
Loading

0 comments on commit fa1844b

Please sign in to comment.