[geohash] Modified geohash implementation (based on python-geohash) with no mallocs
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -1,77 +1,49 @@
|
||||
/*
|
||||
* geohash.h
|
||||
* 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:
|
||||
/*** MIT License
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
* 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.
|
||||
*/
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
// Metric in meters
|
||||
typedef struct GeoBoxDimensionStruct {
|
||||
|
||||
double height;
|
||||
double width;
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
} GeoBoxDimension;
|
||||
#ifndef GEOHASH_H
|
||||
#define GEOHASH_H
|
||||
|
||||
typedef struct GeoCoordStruct {
|
||||
|
||||
double latitude;
|
||||
double longitude;
|
||||
|
||||
double north;
|
||||
double east;
|
||||
double south;
|
||||
double west;
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
GeoBoxDimension dimension;
|
||||
|
||||
} GeoCoord;
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Creates a the hash at the specified precision. If precision is set to 0.
|
||||
* or less than it defaults to 12.
|
||||
*/
|
||||
extern char* geohash_encode(double lat, double lng, int precision);
|
||||
enum {
|
||||
GEOHASH_OK,
|
||||
GEOHASH_NOTSUPPORTED,
|
||||
GEOHASH_INVALIDCODE,
|
||||
GEOHASH_INVALIDARGUMENT,
|
||||
GEOHASH_INTERNALERROR,
|
||||
GEOHASH_NOMEMORY
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns the latitude and longitude used to create the hash along with
|
||||
* the bounding box for the encoded coordinate.
|
||||
*/
|
||||
extern GeoCoord geohash_decode(char* hash);
|
||||
int geohash_encode(double latitude, double longitude, char* r, size_t capacity);
|
||||
int geohash_decode(char* r, size_t length, double *latitude, double *longitude);
|
||||
int geohash_neighbors(char *hashcode, char* dst, size_t dst_length, int *string_count);
|
||||
|
||||
/*
|
||||
* Return an array of geohashes that represent the neighbors of the passed
|
||||
* in value. The neighbors are indexed as followed:
|
||||
*
|
||||
* N, NE, E, SE, S, SW, W, NW
|
||||
* 0, 1, 2, 3, 4, 5, 6, 7
|
||||
*/
|
||||
extern char** geohash_neighbors(char* hash);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Returns the width and height of a precision value.
|
||||
*/
|
||||
extern GeoBoxDimension geohash_dimensions_for_precision(int precision);
|
||||
#endif
|
||||
Reference in New Issue
Block a user