Skip to content
This repository has been archived by the owner on Sep 27, 2023. It is now read-only.

Commit

Permalink
Add support for reverse geocode (#1182)
Browse files Browse the repository at this point in the history
  • Loading branch information
dchassin authored Jun 14, 2022
1 parent a9c8f36 commit 8e6f3ae
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 2 deletions.
6 changes: 6 additions & 0 deletions docs/GLM/Global/Geocode.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ GLM:
~~~
${GEOCODE <latitude>,<longitude>[#<resolution>]}
${GEOCODE <objname>[#<resolution>]}
${GEOCODE <geohash>[.lat|.lon]}
~~~

# Description
Expand All @@ -27,6 +28,8 @@ The default resolution is 5. The resolution corresponds to the following distanc
10 0.0006 km
11 0.000075 km

The reverse conversion transfer the geohash into a latitude/longitude pair. If the `.lat` or `.lon` spec is include, then only the corresponding value is returned.

# Example

The following example prints the geohash codes for a position and an object:
Expand All @@ -44,6 +47,9 @@ object test
}
#print ${GEOCODE 37.5,-122.2#6}
#print ${GEOCODE test#6}
#print ${GEOCODE 9q9j76}
#print ${GEOCODE 9q9j76.lat}
#print ${GEOCODE 9q9j76.lon}
~~~

# See also
Expand Down
12 changes: 12 additions & 0 deletions source/autotest/test_geocode.glm
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,15 @@ object test
#if ${GEOCODE test#6} != 9q9j76
#error geocode "test#6" does not match "9q9j76"
#endif

#if ${GEOCODE 9q9j76} != 37.50,-122.20
#error geocode "9q9j76" does not match "37.50,-122.20"
#endif

#if ${GEOCODE 9q9j76.lat} != 37.50
#error geocode "9q9j76" does not match "37.50"
#endif

#if ${GEOCODE 9q9j76.lon} != -122.20
#error geocode "9q9j76" does not match "-122.20"
#endif
81 changes: 79 additions & 2 deletions source/globals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1438,9 +1438,10 @@ DEPRECATED const char *global_findobj(char *buffer, int size, const char *spec)
return buffer;
}

static const char geocode_decodemap[] = "0123456789bcdefghjkmnpqrstuvwxyz";
static const unsigned char *geocode_encodemap = NULL;
const char *geocode_encode(char *buffer, int len, double lat, double lon, int resolution=12)
{
static const char *base32 = "0123456789bcdefghjkmnpqrstuvwxyz";
if ( len < resolution+1 )
{
output_warning("geocode_encode(buffer=%p, len=%d, lat=%g, lon=%g, resolution=%d): buffer too small for specified resolution, result truncated",
Expand Down Expand Up @@ -1491,7 +1492,7 @@ const char *geocode_encode(char *buffer, int len, double lat, double lon, int re
}
else
{
*geohash++ = base32[ch];
*geohash++ = geocode_decodemap[ch];
i++;
bit = 0;
ch = 0;
Expand All @@ -1501,6 +1502,78 @@ const char *geocode_encode(char *buffer, int len, double lat, double lon, int re
return buffer;
}

DEPRECATED const char *geocode_decode(char *buffer, int size, const char *code)
{
double lat_err = 90, lon_err = 180;
double lat_interval[] = {-lat_err,lat_err};
double lon_interval[] = {-lon_err,lon_err};
bool is_even = true;
size_t maxlen = strlen(geocode_decodemap);
if ( geocode_encodemap == NULL )
{
static unsigned char map[256];
for ( size_t p = 0 ; p < maxlen ; p++ )
{
int c = (int)geocode_decodemap[p];
map[c] = p+1;
if ( c >= 'a' && c <= 'z' )
{
map[c + 'A' - 'a'] = map[c];
}
}
geocode_encodemap = map;
}
const char *c = NULL;
for ( c = code ; *c != '\0' && *c != '.' ; c++ )
{
int cd = geocode_encodemap[(size_t)*c] - 1;
if ( cd < 0 )
{
return NULL;
}
for ( int mask = 16 ; mask > 0 ; mask /= 2 )
{
if ( is_even )
{
lon_err /= 2;
lon_interval[cd&mask?0:1] = (lon_interval[0] + lon_interval[1])/2;
}
else
{
lat_err /= 2;
lat_interval[cd&mask?0:1] = (lat_interval[0] + lat_interval[1])/2;
}
is_even = ! is_even;
}
}
const char *spec = NULL;
if ( *c == '.' )
{
spec = c+1;
}
int res = -(int)log10(lat_err);
if ( spec == NULL )
{
return snprintf(buffer,size,"%.*lf,%.*lf",
res,(lat_interval[0] + lat_interval[1])/2,
res,(lon_interval[0] + lon_interval[1])/2) < size ? buffer : NULL;
}
else if ( strcmp(spec,"lat") == 0 )
{
return snprintf(buffer,size,"%.*lf",
res,(lat_interval[0] + lat_interval[1])/2) < size ? buffer : NULL;
}
else if ( strcmp(spec,"lon") == 0 )
{
return snprintf(buffer,size,"%.*lf",
res,(lon_interval[0] + lon_interval[1])/2) < size ? buffer : NULL;
}
else
{
return NULL;
}
}

DEPRECATED const char *global_geocode(char *buffer, int size, const char *spec)
{
double lat, lon;
Expand All @@ -1520,6 +1593,10 @@ DEPRECATED const char *global_geocode(char *buffer, int size, const char *spec)
return geocode_encode(buffer,size,lat,lon,res);
}
}
else if ( geocode_decode(buffer,size,spec) )
{
return buffer;
}
output_warning("${GEOCODE %s}: geocode spec is not valid",spec);
buffer[0] = '\0';
return buffer;
Expand Down

0 comments on commit 8e6f3ae

Please sign in to comment.