mirror of
https://github.com/php-win-ext/geospatial.git
synced 2026-03-24 00:52:13 +01:00
Coding standards, and a new test
This commit is contained in:
35
README.rst
35
README.rst
@@ -174,25 +174,28 @@ you would use::
|
||||
Geohashing
|
||||
----------
|
||||
|
||||
The `geohash_encode` function can be used to convert GeoJSON Point to a geohash of a specific lenth (in this case, 12)::
|
||||
The `geohash_encode` function can be used to convert GeoJSON Point to a
|
||||
geohash of a specific length (in this case, 12)::
|
||||
|
||||
echo geohash_encode(array('type' => 'Point', 'coordinates' => [16.4, 48.2]), 12);
|
||||
$point = [ 'type' => 'Point', 'coordinates' => [ 16.4, 48.2 ] ];
|
||||
echo geohash_encode( $point, 12 );
|
||||
|
||||
Which outputs::
|
||||
|
||||
u2edjnw17enr
|
||||
u2edjnw17enr
|
||||
|
||||
Similarly, a hashed geopoint can be decoded using `geohash_decode` function::
|
||||
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)
|
||||
}
|
||||
}
|
||||
var_dump(geohash_decode('u2edjnw17enr'));
|
||||
array(2) {
|
||||
["type"]=>
|
||||
string(5) "Point"
|
||||
["coordinates"]=>
|
||||
array(2) {
|
||||
[0]=>
|
||||
float(16.40000006184)
|
||||
[1]=>
|
||||
float(48.199999993667)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef PHP_GEO_ARRAY_H
|
||||
#define PHP_GEO_ARRAY_H
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "geo_array.h"
|
||||
@@ -61,4 +59,3 @@ void geo_array_dtor(geo_array *points)
|
||||
free(points->y);
|
||||
free(points);
|
||||
}
|
||||
#endif /* PHP_GEO_ARRAY_H */
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
| Marcus Deglos <marcus@deglos.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
#ifndef PHP_GEO_ARRAY_H
|
||||
#define PHP_GEO_ARRAY_H
|
||||
|
||||
typedef struct geo_array {
|
||||
double *x;
|
||||
@@ -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 */
|
||||
|
||||
@@ -15,14 +15,13 @@
|
||||
| Authors: Emir Beganovic <emir@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef PHP_GEO_LAT_LONG_H
|
||||
#define PHP_GEO_LAT_LONG_H
|
||||
|
||||
typedef struct {
|
||||
double x;
|
||||
double y;
|
||||
double z;
|
||||
double x;
|
||||
double y;
|
||||
double z;
|
||||
} geo_lat_long;
|
||||
|
||||
#endif
|
||||
|
||||
166
geohash.c
166
geohash.c
@@ -22,125 +22,119 @@
|
||||
#include "geo_lat_long.h"
|
||||
#include "geohash.h"
|
||||
|
||||
#define MAX_LAT 90.0
|
||||
#define MAX_LAT 90.0
|
||||
#define MIN_LAT -90.0
|
||||
|
||||
#define MAX_LONG 180.0
|
||||
#define MAX_LONG 180.0
|
||||
#define MIN_LONG -180.0
|
||||
|
||||
typedef struct interval_string {
|
||||
double high;
|
||||
double low;
|
||||
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 *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;
|
||||
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 = (char*)safe_emalloc(precision, sizeof(char), 1);
|
||||
|
||||
hash[precision] = '\0';
|
||||
steps = precision * 5.0;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
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 (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;
|
||||
}
|
||||
if (!(i % 5)) {
|
||||
hash[(i - 1) / 5] = char_map[hash_char];
|
||||
hash_char = 0;
|
||||
}
|
||||
|
||||
is_even = !is_even;
|
||||
}
|
||||
is_even = !is_even;
|
||||
}
|
||||
|
||||
return hash;
|
||||
return hash;
|
||||
}
|
||||
|
||||
static unsigned int index_for_char(char c, char* string)
|
||||
static unsigned int index_for_char(char c, char *string, size_t string_amount)
|
||||
{
|
||||
unsigned int index = -1;
|
||||
int string_amount = strlen(string);
|
||||
int i;
|
||||
unsigned int index = -1;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < string_amount; i++) {
|
||||
if (c == string[i]) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < string_amount; i++) {
|
||||
if (c == string[i]) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
return index;
|
||||
}
|
||||
|
||||
geo_lat_long php_geo_geohash_decode(char* hash)
|
||||
geo_lat_long php_geo_geohash_decode(char *hash, size_t char_amount)
|
||||
{
|
||||
geo_lat_long coordinate;
|
||||
int char_amount = strlen(hash);
|
||||
geo_lat_long coordinate;
|
||||
|
||||
if (char_amount) {
|
||||
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 charmap_index;
|
||||
double delta;
|
||||
int i, j;
|
||||
int is_even = 1;
|
||||
|
||||
interval_struct lat_interval = { MAX_LAT, MIN_LAT };
|
||||
interval_struct lng_interval = { MAX_LONG, MIN_LONG };
|
||||
interval_struct* interval;
|
||||
for (i = 0; i < char_amount; i++) {
|
||||
charmap_index = index_for_char(hash[i], char_map, char_map_size);
|
||||
|
||||
int is_even = 1;
|
||||
/* Interpret the last 5 bits of the integer */
|
||||
for (j = 0; j < 5; j++) {
|
||||
interval = is_even ? &lng_interval : &lat_interval;
|
||||
|
||||
for (i = 0; i < char_amount; i++) {
|
||||
delta = (interval->high - interval->low) / 2.0;
|
||||
|
||||
charmap_index = index_for_char(hash[i], (char*)char_map);
|
||||
if ((charmap_index << j) & 0x0010) {
|
||||
interval->low += delta;
|
||||
} else {
|
||||
interval->high -= delta;
|
||||
}
|
||||
|
||||
/* Interpret the last 5 bits of the integer */
|
||||
for (j = 0; j < 5; j++) {
|
||||
interval = is_even ? &lng_interval : &lat_interval;
|
||||
is_even = !is_even;
|
||||
}
|
||||
}
|
||||
|
||||
delta = (interval->high - interval->low) / 2.0;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
return coordinate;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
15
geohash.h
15
geohash.h
@@ -15,19 +15,10 @@
|
||||
| Authors: Emir Beganovic <emir@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#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);
|
||||
#endif /* 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);
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
* c-basic-offset: 4
|
||||
* End:
|
||||
* vim600: noet sw=4 ts=4 fdm=marker
|
||||
* vim<600: noet sw=4 ts=4
|
||||
*/
|
||||
#endif /* PHP_GEOHASH_H */
|
||||
|
||||
37
geospatial.c
37
geospatial.c
@@ -369,7 +369,7 @@ double php_geo_vincenty(double from_lat, double from_long, double to_lat, double
|
||||
sinLambda = sin(lambda);
|
||||
cosLambda = cos(lambda);
|
||||
sinSigma = sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
|
||||
(cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) * (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda));
|
||||
(cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) * (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda));
|
||||
cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
|
||||
sigma = atan2(sinSigma, cosSigma);
|
||||
sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
|
||||
@@ -378,14 +378,14 @@ double php_geo_vincenty(double from_lat, double from_long, double to_lat, double
|
||||
C = eli.f / 16.0 * cos2Alpha * (4.0 + eli.f * (4.0 - 3.0 * cos2Alpha));
|
||||
lambdaP = lambda;
|
||||
lambda = L + (1.0 - C) * eli.f * sinAlpha *
|
||||
(sigma + C*sinSigma*(cosof2sigma+C*cosSigma*(-1.0 + 2.0 *cosof2sigma*cosof2sigma)));
|
||||
(sigma + C*sinSigma*(cosof2sigma+C*cosSigma*(-1.0 + 2.0 *cosof2sigma*cosof2sigma)));
|
||||
--loopLimit;
|
||||
} while (fabs(lambda - lambdaP) > precision && loopLimit > 0);
|
||||
uSq = cos2Alpha * (eli.a * eli.a - eli.b * eli.b) / (eli.b * eli.b);
|
||||
A = 1.0 + uSq / 16384.0 * (4096.0 + uSq * (-768.0 + uSq * (320.0 - 175.0 * uSq)));
|
||||
B = uSq / 1024.0 * ( 256.0 + uSq * (-128.0 + uSq * (74.0 - 47.0 * uSq)));
|
||||
deltaSigma = B * sinSigma * (cosof2sigma+B/4.0 * (cosSigma * (-1.0 + 2.0 *cosof2sigma*cosof2sigma)-
|
||||
B / 6.0 * cosof2sigma * (-3.0 + 4.0 *sinSigma*sinSigma) * (-3.0 + 4.0 *cosof2sigma*cosof2sigma)));
|
||||
deltaSigma = B * sinSigma * (cosof2sigma+B/4.0 * (cosSigma * (-1.0 + 2.0 *cosof2sigma*cosof2sigma) -
|
||||
B / 6.0 * cosof2sigma * (-3.0 + 4.0 *sinSigma*sinSigma) * (-3.0 + 4.0 *cosof2sigma*cosof2sigma)));
|
||||
s = eli.b * A * (sigma - deltaSigma);
|
||||
s = floor(s * 1000) / 1000;
|
||||
return s;
|
||||
@@ -729,7 +729,7 @@ double php_initial_bearing(double from_lat, double from_long, double to_lat, dou
|
||||
/*
|
||||
var y = Math.sin(dLon) * Math.cos(lat2);
|
||||
var x = Math.cos(lat1)*Math.sin(lat2) -
|
||||
Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
|
||||
Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
|
||||
var brng = Math.atan2(y, x).toDeg();
|
||||
*/
|
||||
double x, y;
|
||||
@@ -1087,15 +1087,14 @@ PHP_FUNCTION(interpolate_polygon)
|
||||
*/
|
||||
PHP_FUNCTION(geohash_encode)
|
||||
{
|
||||
double longitude, latitude;
|
||||
|
||||
double longitude, latitude;
|
||||
#if PHP_VERSION_ID >= 70000
|
||||
zend_long precision = 12;
|
||||
zend_long precision = 12;
|
||||
#else
|
||||
long precision = 12;
|
||||
long precision = 12;
|
||||
#endif
|
||||
zval *geojson;
|
||||
char* hash;
|
||||
zval *geojson;
|
||||
char *hash;
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "al", &geojson, &precision) == FAILURE) {
|
||||
return;
|
||||
@@ -1105,13 +1104,12 @@ PHP_FUNCTION(geohash_encode)
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
|
||||
hash = php_geo_geohash_encode(latitude, longitude, precision);
|
||||
#if PHP_VERSION_ID < 70000
|
||||
RETVAL_STRING(hash, 0);
|
||||
#else
|
||||
#if PHP_VERSION_ID >= 70000
|
||||
RETVAL_STRING(hash);
|
||||
efree(hash);
|
||||
#else
|
||||
RETVAL_STRING(hash, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1119,19 +1117,18 @@ PHP_FUNCTION(geohash_encode)
|
||||
*/
|
||||
PHP_FUNCTION(geohash_decode)
|
||||
{
|
||||
char* hash;
|
||||
|
||||
char *hash;
|
||||
#if PHP_VERSION_ID >= 70000
|
||||
size_t hash_len;
|
||||
size_t hash_len;
|
||||
#else
|
||||
int hash_len;
|
||||
int hash_len;
|
||||
#endif
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &hash, &hash_len) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
geo_lat_long area = php_geo_geohash_decode(hash);
|
||||
geo_lat_long area = php_geo_geohash_decode(hash, hash_len);
|
||||
|
||||
retval_point_from_coordinates(return_value, area.y, area.x);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ var_dump(geohash_decode('ezs42'));
|
||||
var_dump(geohash_decode('.'));
|
||||
|
||||
var_dump(geohash_decode('u2edjnw17enr'));
|
||||
|
||||
var_dump(geohash_decode('zzzzzzzzzzzz'));
|
||||
?>
|
||||
--EXPECT--
|
||||
array(2) {
|
||||
@@ -189,3 +191,14 @@ array(2) {
|
||||
float(48.199999993667)
|
||||
}
|
||||
}
|
||||
array(2) {
|
||||
["type"]=>
|
||||
string(5) "Point"
|
||||
["coordinates"]=>
|
||||
array(2) {
|
||||
[0]=>
|
||||
float(179.99999983236)
|
||||
[1]=>
|
||||
float(89.999999916181)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user