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

Add support for reverse geocode #1182

Merged
merged 2 commits into from
Jun 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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