[geohash] Modified geohash implementation (based on python-geohash) with no mallocs

This commit is contained in:
Al
2015-07-04 00:30:35 -04:00
parent f825dcb939
commit 3f07cc6c71
2 changed files with 519 additions and 326 deletions

View File

@@ -1,293 +1,514 @@
/*
* geohash.c
* libgeohash
*
* Created by Derek Smith on 10/6/09.
* Copyright (c) 2010, SimpleGeo
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer. Redistributions in binary form must
* reproduce the above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of the SimpleGeo nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "geohash.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
// on Windows, __STDC_IEC_559__ not defined
#define MAX_LAT 90.0
#define MIN_LAT -90.0
#if defined(_MSC_VER) && (_MSC_VER <= 1500)
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int64 uint64_t;
#define UINT64_C(C) ((uint64_t) C ## ULL)
#else
#define __STDC_CONSTANT_MACROS 1
#include <stdint.h>
#endif
#define MAX_LONG 180.0
#define MIN_LONG -180.0
#define NORTH 0
#define EAST 1
#define SOUTH 2
#define WEST 3
static inline uint16_t interleave(uint8_t upper, uint8_t lower) {
static const uint16_t map[256] = {
0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011,
0x0014, 0x0015, 0x0040, 0x0041, 0x0044, 0x0045,
0x0050, 0x0051, 0x0054, 0x0055, 0x0100, 0x0101,
0x0104, 0x0105, 0x0110, 0x0111, 0x0114, 0x0115,
0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151,
0x0154, 0x0155, 0x0400, 0x0401, 0x0404, 0x0405,
0x0410, 0x0411, 0x0414, 0x0415, 0x0440, 0x0441,
0x0444, 0x0445, 0x0450, 0x0451, 0x0454, 0x0455,
0x0500, 0x0501, 0x0504, 0x0505, 0x0510, 0x0511,
0x0514, 0x0515, 0x0540, 0x0541, 0x0544, 0x0545,
0x0550, 0x0551, 0x0554, 0x0555, 0x1000, 0x1001,
0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015,
0x1040, 0x1041, 0x1044, 0x1045, 0x1050, 0x1051,
0x1054, 0x1055, 0x1100, 0x1101, 0x1104, 0x1105,
0x1110, 0x1111, 0x1114, 0x1115, 0x1140, 0x1141,
0x1144, 0x1145, 0x1150, 0x1151, 0x1154, 0x1155,
0x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411,
0x1414, 0x1415, 0x1440, 0x1441, 0x1444, 0x1445,
0x1450, 0x1451, 0x1454, 0x1455, 0x1500, 0x1501,
0x1504, 0x1505, 0x1510, 0x1511, 0x1514, 0x1515,
0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551,
0x1554, 0x1555, 0x4000, 0x4001, 0x4004, 0x4005,
0x4010, 0x4011, 0x4014, 0x4015, 0x4040, 0x4041,
0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055,
0x4100, 0x4101, 0x4104, 0x4105, 0x4110, 0x4111,
0x4114, 0x4115, 0x4140, 0x4141, 0x4144, 0x4145,
0x4150, 0x4151, 0x4154, 0x4155, 0x4400, 0x4401,
0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415,
0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451,
0x4454, 0x4455, 0x4500, 0x4501, 0x4504, 0x4505,
0x4510, 0x4511, 0x4514, 0x4515, 0x4540, 0x4541,
0x4544, 0x4545, 0x4550, 0x4551, 0x4554, 0x4555,
0x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011,
0x5014, 0x5015, 0x5040, 0x5041, 0x5044, 0x5045,
0x5050, 0x5051, 0x5054, 0x5055, 0x5100, 0x5101,
0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115,
0x5140, 0x5141, 0x5144, 0x5145, 0x5150, 0x5151,
0x5154, 0x5155, 0x5400, 0x5401, 0x5404, 0x5405,
0x5410, 0x5411, 0x5414, 0x5415, 0x5440, 0x5441,
0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455,
0x5500, 0x5501, 0x5504, 0x5505, 0x5510, 0x5511,
0x5514, 0x5515, 0x5540, 0x5541, 0x5544, 0x5545,
0x5550, 0x5551, 0x5554, 0x5555
};
return (map[upper]<<1)+map[lower];
}
#define LENGTH_OF_DEGREE 111100 // meters
typedef struct IntervalStruct {
static inline void deinterleave(uint16_t interleaved, uint8_t *upper, uint8_t *lower) {
*upper = *lower = 0;
for (int i=7; i>=0; i--) {
*upper = (*upper<<1) + ((interleaved>>(i*2+1))&0x01);
*lower = (*lower<<1) + ((interleaved>>(i*2))&0x01);
}
}
/**
* map double[-1.0, 1.0) into uint64_t
*/
static inline int double_to_i64(double in, uint64_t *out) {
if (in<-1.0 || 1.0<=in) {
return 0;
}
union {
double d; // assuming IEEE 754-1985 binary64. This might not be true on some CPU (I don't know which).
// formally, we should use unsigned char for type-punning (see C99 ISO/IEC 9899:201x spec 6.2.6)
uint64_t i64;
} x;
x.d = in;
int sign = x.i64 >> 63;
int exp = (x.i64 >> 52) & 0x7FF;
if (exp==0) {
*out = UINT64_C(0x8000000000000000);
return !0;
} else if(exp==0x7FF) {
return 0;
}
double high;
double low;
x.i64 &= UINT64_C(0x000FFFFFFFFFFFFF);
x.i64 |= UINT64_C(0x0010000000000000);
int shift = exp - 0x3FF + 11;
if (shift > 0) {
x.i64 <<= shift;
} else {
x.i64 >>= -shift;
}
if (sign) {
x.i64 = UINT64_C(0x8000000000000000) - x.i64;
} else {
x.i64 += UINT64_C(0x8000000000000000);
}
*out = x.i64;
} Interval;
return !0;
}
/**
* map uint64_t into double[-1.0, 1.0)
*/
static inline void i64_to_double(uint64_t in, double *out) {
union {
double d; // assuming IEEE 754-1985 binary64. This might not be true on some CPU (I don't know which).
// formally, we should use unsigned char for type-punning (see C99 ISO/IEC 9899:201x spec 6.2.6)
uint64_t i64;
} x;
if (in==UINT64_C(0x8000000000000000)) {
*out = 0.0;
return;
}
int sign = 0;
if (in < UINT64_C(0x8000000000000000)) {
sign = 1; // negative. -1.0 -- 0.0
in = UINT64_C(0x8000000000000000) - in;
} else {
in -= UINT64_C(0x8000000000000000);
}
int i;
for (i=0;i<64;i++) {
if (in>>(63-i)) { break; }
}
if (i>11) {
x.i64 = in<<(i-11);
} else {
x.i64 = in>>(11-i);
}
x.i64 = ((UINT64_C(0x3FF) - i)<<52) + (x.i64 & UINT64_C(0x000FFFFFFFFFFFFF));
if (sign) {
x.i64 |= UINT64_C(0x8000000000000000);
}
*out = x.d;
}
/* Normal 32 characer map used for geohashing */
static char char_map[32] = "0123456789bcdefghjkmnpqrstuvwxyz";
static int interleaved_to_geohashstr(uint16_t *interleaved, size_t length, char *dst, size_t dst_length) {
static const char *map="0123456789bcdefghjkmnpqrstuvwxyz";
unsigned char *w = (unsigned char *)dst;
uint16_t *i = interleaved;
for (unsigned int j = 0; j < dst_length / 16; j++) {
w[ 0] = (unsigned char)( i[0]>>11);
w[ 1] = (unsigned char)( i[0]>>6);
w[ 2] = (unsigned char)( i[0]>>1);
w[ 3] = (unsigned char)((i[1]>>12) + (i[0]<<4));
w[ 4] = (unsigned char)( i[1]>>7);
w[ 5] = (unsigned char)( i[1]>>2);
w[ 6] = (unsigned char)((i[2]>>13) + (i[1]<<3));
w[ 7] = (unsigned char)( i[2]>>8);
w[ 8] = (unsigned char)( i[2]>>3);
w[ 9] = (unsigned char)((i[3]>>14) + (i[2]<<2));
w[10] = (unsigned char)( i[3]>>9);
w[11] = (unsigned char)( i[3]>>4);
w[12] = (unsigned char)((i[4]>>15) + (i[3]<<1));
w[13] = (unsigned char)( i[4]>>10);
w[14] = (unsigned char)( i[4]>>5);
w[15] = (unsigned char)( i[4]);
i += 5;
w += 16;
}
for (unsigned int j=0; j < dst_length % 16; j++) {
if (j == 0) w[ 0] = (unsigned char)( i[0]>>11);
else if (j == 1) w[ 1] = (unsigned char)( i[0]>>6);
else if (j == 2) w[ 2] = (unsigned char)( i[0]>>1);
else if (j == 3) w[ 3] = (unsigned char)((i[1]>>12) + (i[0]<<4));
else if (j == 4) w[ 4] = (unsigned char)( i[1]>>7);
else if (j == 5) w[ 5] = (unsigned char)( i[1]>>2);
else if (j == 6) w[ 6] = (unsigned char)((i[2]>>13) + (i[1]<<3));
else if (j == 7) w[ 7] = (unsigned char)( i[2]>>8);
else if (j == 8) w[ 8] = (unsigned char)( i[2]>>3);
else if (j == 9) w[ 9] = (unsigned char)((i[3]>>14) + (i[2]<<2));
else if (j == 10) w[10] = (unsigned char)( i[3]>>9);
else if (j == 11) w[11] = (unsigned char)( i[3]>>4);
else if (j == 12) w[12] = (unsigned char)((i[4]>>15) + (i[3]<<1));
else if (j == 13) w[13] = (unsigned char)( i[4]>>10);
else if (j == 14) w[14] = (unsigned char)( i[4]>>5);
else if (j == 15) w[15] = (unsigned char)( i[4]);
}
for (unsigned int j = 0; j < dst_length; j++) {
dst[j] = map[dst[j] & 0x1F];
}
return GEOHASH_OK;
}
/*
* The follow character maps were created by Dave Troy and used in his Javascript Geohashing
* library. http://github.com/davetroy/geohash-js
*/
static char *even_neighbors[] = {"p0r21436x8zb9dcf5h7kjnmqesgutwvy",
"bc01fg45238967deuvhjyznpkmstqrwx",
"14365h7k9dcfesgujnmqp0r2twvyx8zb",
"238967debc01fg45kmstqrwxuvhjyznp"
};
latitude must be in [-90.0, 90.0) and longitude must be in [-180.0 180.0)
*/
int geohash_encode(double latitude, double longitude, char *r, size_t capacity) {
uint64_t lat64, lon64;
uint16_t interleaved[9];
if (capacity > 27) {
return GEOHASH_INVALIDARGUMENT;
}
char lr[capacity];
memset(interleaved, 0, sizeof(interleaved));
static char *odd_neighbors[] = {"bc01fg45238967deuvhjyznpkmstqrwx",
"p0r21436x8zb9dcf5h7kjnmqesgutwvy",
"238967debc01fg45kmstqrwxuvhjyznp",
"14365h7k9dcfesgujnmqp0r2twvyx8zb"
};
static char *even_borders[] = {"prxz", "bcfguvyz", "028b", "0145hjnp"};
static char *odd_borders[] = {"bcfguvyz", "prxz", "0145hjnp", "028b"};
unsigned int index_for_char(char c, char *string) {
if (!double_to_i64(latitude/90.0, &lat64) || !double_to_i64(longitude/180.0, &lon64)) {
return GEOHASH_INVALIDARGUMENT;
}
for (int i=0; i<8; i++) {
interleaved[7-i] = interleave((uint8_t)(lon64>>(i*8)), (uint8_t)(lat64>>(i*8)));
}
int index = -1;
int string_amount = strlen(string);
int i;
for(i = 0; i < string_amount; i++) {
if(c == string[i]) {
index = i;
int ret = GEOHASH_OK;
if ((ret = interleaved_to_geohashstr(interleaved, 8, lr, capacity - 1)) != GEOHASH_OK) {
return ret;
}
lr[capacity - 1] = '\0';
if (0 < capacity) {
memcpy(r, (const char*)lr, capacity-1);
r[capacity - 1] = '\0';
}
return GEOHASH_OK;
}
/**
* handle geohash string decoding operation
*/
static int geohashstr_to_interleaved(char *r, size_t length, uint16_t *interleaved, size_t dst_count) {
static const unsigned char map[128] = {
'|', '|', '|', '|', '|', '|', '|', '|',
'|', '|', '|', '|', '|', '|', '|', '|',
'|', '|', '|', '|', '|', '|', '|', '|',
'|', '|', '|', '|', '|', '|', '|', '|',
'|', '|', '|', '|', '|', '|', '|', '|',
'|', '|', '|', '|', '|', '|', '|', '|',
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, '|', '|', '|', '|', '|', '|',
'|', '|', 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, '|', 0x11, 0x12, '|', 0x13, 0x14, '|',
0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C,
0x1D, 0x1E, 0x1F, '|', '|', '|', '|', '|',
'|', '|', 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, '|', 0x11, 0x12, '|', 0x13, 0x14, '|',
0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C,
0x1D, 0x1E, 0x1F, '|', '|', '|', '|', '|',
};
for (unsigned int i=0; i<length; i++) {
if (r[i]==0) {
length = i;
break;
} else if(r[i]<0 || map[(unsigned char)r[i]]=='|') {
return GEOHASH_INVALIDCODE;
}
}
if (dst_count*16 < length*5) {
return GEOHASH_INTERNALERROR;
}
for (unsigned int j=0; j<dst_count; j++) {
interleaved[j]=0;
}
uint16_t *i = interleaved;
unsigned char *c = (unsigned char*)r;
for (unsigned int j=0; j<length/16; j++) {
i[0] = (map[c[ 0]]<<11) + (map[c[ 1]]<< 6) + (map[c[ 2]]<<1) + (map[c[ 3]]>>4);
i[1] = (map[c[ 3]]<<12) + (map[c[ 4]]<< 7) + (map[c[ 5]]<<2) + (map[c[ 6]]>>3);
i[2] = (map[c[ 6]]<<13) + (map[c[ 7]]<< 8) + (map[c[ 8]]<<3) + (map[c[ 9]]>>2);
i[3] = (map[c[ 9]]<<14) + (map[c[10]]<< 9) + (map[c[11]]<<4) + (map[c[12]]>>1);
i[4] = (map[c[12]]<<15) + (map[c[13]]<<10) + (map[c[14]]<<5) + (map[c[15]]>>0);
i+=5;
c+=16;
}
for (unsigned int j=0; j<length%16; j++) {
if(j== 0) i[0] = map[c[ 0]]<<11;
if(j== 1) i[0] += map[c[ 1]]<< 6;
if(j== 2) i[0] += map[c[ 2]]<< 1;
if(j== 3) i[0] += map[c[ 3]]>> 4;
if(j== 4) i[1] += map[c[ 4]]<< 7;
if(j== 5) i[1] += map[c[ 5]]<< 2;
if(j== 6) i[1] += map[c[ 6]]>> 3;
if(j== 7) i[2] += map[c[ 7]]<< 8;
if(j== 8) i[2] += map[c[ 8]]<< 3;
if(j== 9) i[2] += map[c[ 9]]>> 2;
if(j==10) i[3] += map[c[10]]<< 9;
if(j==11) i[3] += map[c[11]]<< 4;
if(j==12) i[3] += map[c[12]]>> 1;
if(j==13) i[4] += map[c[13]]<<10;
if(j==14) i[4] += map[c[14]]<< 5;
if(j==15) i[4] += map[c[15]]>> 0;
}
return GEOHASH_OK;
}
/*
(latitude, longitude) will be that of south west point.
*/
int geohash_decode(char *r, size_t length, double *latitude, double *longitude) {
uint16_t intr_auto[8];
uint16_t *interleaved = intr_auto;
size_t intr_length = length*5/16+1;
int intr_free = 0;
if (intr_length > 8) {
interleaved = (uint16_t*)malloc(sizeof(uint16_t)*intr_length);
if (!interleaved) {
return GEOHASH_NOMEMORY;
}
intr_free = 1;
} else {
intr_length = 8;
}
int ret = GEOHASH_OK;
if ((ret=geohashstr_to_interleaved(r, length, interleaved, intr_length)) != GEOHASH_OK) {
return ret;
}
uint64_t lat64=0;
uint64_t lon64=0;
for (int i=0; i<8; i++) {
uint8_t upper, lower;
deinterleave(interleaved[i], &upper, &lower);
lon64 = (lon64<<8)+upper;
lat64 = (lat64<<8)+lower;
}
if (intr_free) {
free(interleaved);
}
double t;
i64_to_double(lat64, &t);
*latitude = t*90.0;
i64_to_double(lon64, &t);
*longitude = t*180.0;
return GEOHASH_OK;
}
/**
* compare two uint8_t array of variable sized integers.
*/
static int uint8s_cmp(uint8_t *src, uint8_t *dst, size_t length) {
if (length==0) { return 0; }
unsigned int i=0;
for (i=0; i<(length-1)/8; i++) {
if (src[i] != dst[i]) {
return (int)(src[i] - dst[i]);
}
}
uint8_t cell_offset = (8-length%8)%8;
return (int)((src[i]>>cell_offset) - (dst[i]>>cell_offset));
}
/**
* plus minus operations for uint8_t array of variable sized integer.
*/
static int uint8s_plus_minus(uint8_t *src, uint8_t *dst, size_t length, int plus) {
if (length==0) {
return 0;
}
unsigned int cell = (length-1)/8;
for (unsigned int i=0; i<cell; i++) {
dst[cell] = src[cell];
}
uint8_t cell_offset = (8-length%8)%8;
int up = 1;
while (up) {
uint8_t t;
up = 0;
if (plus) {
t = src[cell] + (1<<cell_offset);
if ((src[cell]&0x80) && !(t&0x80)) {
up = 1;
}
} else {
t = src[cell] - (1<<cell_offset);
if (!(src[cell]&0x80) && (t&0x80)) {
up = 1;
}
}
dst[cell] = t;
cell_offset = 0;
if (cell == 0) {
break;
}
cell--;
}
return index;
return !0;
}
char* get_neighbor(char *hash, int direction) {
int hash_length = strlen(hash);
char last_char = hash[hash_length - 1];
int is_odd = hash_length % 2;
char **border = is_odd ? odd_borders : even_borders;
char **neighbor = is_odd ? odd_neighbors : even_neighbors;
char *base = malloc(sizeof(char) * 1);
base[0] = '\0';
strncat(base, hash, hash_length - 1);
if(index_for_char(last_char, border[direction]) != -1)
base = get_neighbor(base, direction);
int neighbor_index = index_for_char(last_char, neighbor[direction]);
last_char = char_map[neighbor_index];
char *last_hash = malloc(sizeof(char) * 2);
last_hash[0] = last_char;
last_hash[1] = '\0';
strcat(base, last_hash);
free(last_hash);
return base;
}
char* geohash_encode(double lat, double lng, int precision) {
if(precision < 1 || precision > 12)
precision = 6;
char* hash = NULL;
if(lat <= 90.0 && lat >= -90.0 && lng <= 180.0 && lng >= -180.0) {
hash = (char*)malloc(sizeof(char) * (precision + 1));
hash[precision] = '\0';
precision *= 5.0;
Interval lat_interval = {MAX_LAT, MIN_LAT};
Interval lng_interval = {MAX_LONG, MIN_LONG};
Interval *interval;
double coord, mid;
int is_even = 1;
unsigned int hashChar = 0;
int i;
for(i = 1; i <= precision; i++) {
if(is_even) {
interval = &lng_interval;
coord = lng;
} else {
interval = &lat_interval;
coord = lat;
}
mid = (interval->low + interval->high) / 2.0;
hashChar = hashChar << 1;
if(coord > mid) {
interval->low = mid;
hashChar |= 0x01;
} else
interval->high = mid;
if(!(i % 5)) {
hash[(i - 1) / 5] = char_map[hashChar];
hashChar = 0;
}
is_even = !is_even;
}
static int neighbors(uint16_t *interleaved, size_t bitlength, uint16_t *dst, size_t dst_length, size_t *dst_count) {
size_t interleaved_length = 0;
while (interleaved_length*16 < bitlength) {
interleaved_length++;
}
return hash;
}
if (dst_length<interleaved_length*8) {
return GEOHASH_INTERNALERROR;
}
uint8_t latlons[interleaved_length*6];
GeoCoord geohash_decode(char *hash) {
uint8_t *lat_8s = latlons;
uint8_t *lon_8s = latlons + interleaved_length*3;
GeoCoord coordinate = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
unsigned int lat_len = bitlength/2;
unsigned int lon_len = bitlength/2+bitlength%2;
if(hash) {
int char_amount = strlen(hash);
if(char_amount) {
unsigned int char_mapIndex;
Interval lat_interval = {MAX_LAT, MIN_LAT};
Interval lng_interval = {MAX_LONG, MIN_LONG};
Interval *interval;
int is_even = 1;
double delta;
int i, j;
for(i = 0; i < char_amount; i++) {
char_mapIndex = index_for_char(hash[i], (char*)char_map);
if(char_mapIndex < 0)
break;
// 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((char_mapIndex << j) & 0x0010)
interval->low += delta;
else
interval->high -= delta;
is_even = !is_even;
}
for (unsigned int i=0; i<interleaved_length; i++) {
deinterleave(interleaved[i], lon_8s+i, lat_8s+i);
lat_8s[i+interleaved_length*2] = lat_8s[i+interleaved_length] = lat_8s[i];
lon_8s[i+interleaved_length*2] = lon_8s[i+interleaved_length] = lon_8s[i];
}
uint8_t *lats[3] = {lat_8s, lat_8s+interleaved_length, lat_8s+interleaved_length*2};
uint8_t *lons[3] = {lon_8s, lon_8s+interleaved_length, lon_8s+interleaved_length*2};
if (uint8s_plus_minus(lats[0], lats[1], lat_len, 0)) {
if ((lats[1][0]&0x80) && !(lats[0][0]&0x80)) {
for (unsigned int i=0; i<interleaved_length; i++) {
lats[1][i] = lats[0][i]; // this cause skip
}
coordinate.latitude = lat_interval.high - ((lat_interval.high - lat_interval.low) / 2.0);
coordinate.longitude = lng_interval.high - ((lng_interval.high - lng_interval.low) / 2.0);
coordinate.north = lat_interval.high;
coordinate.east = lng_interval.high;
coordinate.south = lat_interval.low;
coordinate.west = lng_interval.low;
}
}
if (uint8s_plus_minus(lats[0], lats[2], lat_len, 1)) {
if (!(lats[2][0]&0x80) && (lats[0][0]&0x80)) {
for (unsigned int i=0; i<interleaved_length; i++) {
lats[2][i] = lats[1][i]; // this cause skip
}
}
}
uint8s_plus_minus(lons[0], lons[1], lon_len, 0);
uint8s_plus_minus(lons[0], lons[2], lon_len, 1);
return coordinate;
size_t neighbour_count = 0;
for (unsigned int i=0;i<3;i++) {
if (i>0 && uint8s_cmp(lats[i-1], lats[i], lat_len)==0) {
continue;
}
for (int j=0;j<3;j++) {
if (j>0 && uint8s_cmp(lons[j-1], lons[j], lon_len)==0) {
continue;
}
if (i==0 && j==0) {
continue;
}
for (unsigned int k=0; k<interleaved_length; k++) {
dst[interleaved_length*neighbour_count+k] = interleave(lons[j][k], lats[i][k]);
}
neighbour_count++;
}
}
if (dst_count) {
*dst_count = neighbour_count;
}
return GEOHASH_OK;
}
int geohash_neighbors(char *hashcode, char *dst, size_t dst_length, int *string_count) {
int ret = GEOHASH_OK;
size_t hashcode_length = strlen(hashcode);
size_t interleaved_length = 0;
while (interleaved_length*16 < hashcode_length*5) {
interleaved_length++;
}
char** geohash_neighbors(char *hash) {
char** neighbors = NULL;
if(hash) {
// N, NE, E, SE, S, SW, W, NW
neighbors = (char**)malloc(sizeof(char*) * 8);
neighbors[0] = get_neighbor(hash, NORTH);
neighbors[1] = get_neighbor(neighbors[0], EAST);
neighbors[2] = get_neighbor(hash, EAST);
neighbors[3] = get_neighbor(neighbors[2], SOUTH);
neighbors[4] = get_neighbor(hash, SOUTH);
neighbors[5] = get_neighbor(neighbors[4], WEST);
neighbors[6] = get_neighbor(hash, WEST);
neighbors[7] = get_neighbor(neighbors[6], NORTH);
uint16_t interleaved[interleaved_length * 9];
if ((ret=geohashstr_to_interleaved(hashcode, hashcode_length, interleaved, interleaved_length)) != GEOHASH_OK) {
return ret;
}
return neighbors;
}
size_t dst_count = 0;
uint16_t *intr_dst = interleaved + interleaved_length;
if ((ret = neighbors(interleaved, hashcode_length*5, intr_dst, interleaved_length*8, &dst_count) != GEOHASH_OK)) {
return ret;
}
size_t blen = hashcode_length;
GeoBoxDimension geohash_dimensions_for_precision(int precision) {
GeoBoxDimension dimensions = {0.0, 0.0};
if(precision > 0) {
int lat_times_to_cut = precision * 5 / 2;
int lng_times_to_cut = precision * 5 / 2 + (precision % 2 ? 1 : 0);
double width = 360.0;
double height = 180.0;
int i;
for(i = 0; i < lat_times_to_cut; i++)
height /= 2.0;
for(i = 0; i < lng_times_to_cut; i++)
width /= 2.0;
dimensions.width = width;
dimensions.height = height;
}
return dimensions;
}
char buffer[blen];
memset(buffer, 0, blen);
size_t t = hashcode_length + 1;
for (unsigned int i=0; i<dst_count; i++) {
if ((ret = interleaved_to_geohashstr(intr_dst+i*interleaved_length, interleaved_length, buffer, blen)) != GEOHASH_OK) {
return ret;
}
buffer[hashcode_length] = '\0';
memcpy(dst+i*t, buffer, t);
}
if (string_count) {
*string_count = dst_count;
}
return GEOHASH_OK;
}