mirror of
https://github.com/fluencelabs/redis
synced 2025-03-19 17:10:50 +00:00
Geo: JSON features removed
The command can only return data in the normal Redis protocol. It is up to the caller to translate to JSON if needed.
This commit is contained in:
parent
f193b3caa8
commit
b18c68aa7f
@ -117,7 +117,7 @@ endif
|
||||
|
||||
REDIS_SERVER_NAME=redis-server
|
||||
REDIS_SENTINEL_NAME=redis-sentinel
|
||||
REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o geo.o zset.o geojson.o
|
||||
REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o geo.o zset.o
|
||||
REDIS_GEOHASH_OBJ=../deps/geohash-int/geohash.o ../deps/geohash-int/geohash_helper.o
|
||||
REDIS_CLI_NAME=redis-cli
|
||||
REDIS_CLI_OBJ=anet.o sds.o adlist.o redis-cli.o zmalloc.o release.o anet.o ae.o crc64.o
|
||||
|
153
src/geo.c
153
src/geo.c
@ -29,7 +29,6 @@
|
||||
|
||||
#include "geo.h"
|
||||
#include "geohash_helper.h"
|
||||
#include "geojson.h"
|
||||
#include "zset.h"
|
||||
|
||||
/* ====================================================================
|
||||
@ -110,49 +109,15 @@ static double extractDistanceOrReply(redisClient *c, robj **argv,
|
||||
return distance * to_meters;
|
||||
}
|
||||
|
||||
/* Output Reply Helper */
|
||||
static void latLongToGeojsonAndReply(redisClient *c, struct geojsonPoint *gp,
|
||||
char *units) {
|
||||
sds geojson = geojsonLatLongToPointFeature(
|
||||
gp->latitude, gp->longitude, gp->set, gp->member, gp->dist, units);
|
||||
|
||||
addReplyBulkCBuffer(c, geojson, sdslen(geojson));
|
||||
sdsfree(geojson);
|
||||
}
|
||||
|
||||
/* Output Reply Helper */
|
||||
static void decodeGeohashToGeojsonBoundsAndReply(redisClient *c,
|
||||
uint64_t hashbits,
|
||||
struct geojsonPoint *gp) {
|
||||
GeoHashArea area = {{0,0},{0,0},{0,0}};
|
||||
GeoHashBits hash = { .bits = hashbits, .step = GEO_STEP_MAX };
|
||||
|
||||
geohashDecodeWGS84(hash, &area);
|
||||
|
||||
sds geojson = geojsonBoxToPolygonFeature(
|
||||
area.latitude.min, area.longitude.min, area.latitude.max,
|
||||
area.longitude.max, gp->set, gp->member);
|
||||
addReplyBulkCBuffer(c, geojson, sdslen(geojson));
|
||||
sdsfree(geojson);
|
||||
}
|
||||
|
||||
/* The defailt addReplyDouble has too much accuracy. We use this
|
||||
* for returning location distances. "5.21 meters away" is nicer
|
||||
* than "5.2144992818115 meters away." */
|
||||
static inline void addReplyDoubleNicer(redisClient *c, double d) {
|
||||
static inline void addReplyDoubleMeters(redisClient *c, double d) {
|
||||
char dbuf[128] = { 0 };
|
||||
int dlen = snprintf(dbuf, sizeof(dbuf), "%.2f", d);
|
||||
addReplyBulkCBuffer(c, dbuf, dlen);
|
||||
}
|
||||
|
||||
/* Output Reply Helper */
|
||||
static void replyGeojsonCollection(redisClient *c, struct geojsonPoint *gp,
|
||||
long result_length, char *units) {
|
||||
sds geojson = geojsonFeatureCollection(gp, result_length, units);
|
||||
addReplyBulkCBuffer(c, geojson, sdslen(geojson));
|
||||
sdsfree(geojson);
|
||||
}
|
||||
|
||||
/* geohash range+zset access helper */
|
||||
/* Obtain all members between the min/max of this geohash bounding box. */
|
||||
/* Returns list of results. List must be listRelease()'d later. */
|
||||
@ -291,7 +256,7 @@ static int publishLocationUpdate(const sds zset, const sds member,
|
||||
|
||||
/* Sort comparators for qsort() */
|
||||
static int sort_gp_asc(const void *a, const void *b) {
|
||||
const struct geojsonPoint *gpa = a, *gpb = b;
|
||||
const struct geoPoint *gpa = a, *gpb = b;
|
||||
/* We can't do adist - bdist because they are doubles and
|
||||
* the comparator returns an int. */
|
||||
if (gpa->dist > gpb->dist)
|
||||
@ -431,12 +396,8 @@ static void geoRadiusGeneric(redisClient *c, int type) {
|
||||
return;
|
||||
}
|
||||
|
||||
sds units = c->argv[base_args - 2 + 1]->ptr;
|
||||
|
||||
/* Discover and populate all optional parameters. */
|
||||
int withdist = 0, withhash = 0, withcoords = 0,
|
||||
withgeojson = 0, withgeojsonbounds = 0,
|
||||
withgeojsoncollection = 0, noproperties = 0;
|
||||
int withdist = 0, withhash = 0, withcoords = 0, noproperties = 0;
|
||||
int sort = SORT_NONE;
|
||||
if (c->argc > base_args) {
|
||||
int remaining = c->argc - base_args;
|
||||
@ -448,14 +409,6 @@ static void geoRadiusGeneric(redisClient *c, int type) {
|
||||
withhash = 1;
|
||||
else if (!strncasecmp(arg, "withcoord", 9))
|
||||
withcoords = 1;
|
||||
else if (!strncasecmp(arg, "withgeojsonbound", 16))
|
||||
withgeojsonbounds = 1;
|
||||
else if (!strncasecmp(arg, "withgeojsoncollection", 21))
|
||||
withgeojsoncollection = 1;
|
||||
else if (!strncasecmp(arg, "withgeo", 7) ||
|
||||
!strcasecmp(arg, "geojson") || !strcasecmp(arg, "json") ||
|
||||
!strcasecmp(arg, "withjson"))
|
||||
withgeojson = 1;
|
||||
else if (!strncasecmp(arg, "noprop", 6) ||
|
||||
!strncasecmp(arg, "withoutprop", 11))
|
||||
noproperties = 1;
|
||||
@ -471,8 +424,6 @@ static void geoRadiusGeneric(redisClient *c, int type) {
|
||||
}
|
||||
}
|
||||
|
||||
int withgeo = withgeojsonbounds || withgeojsoncollection || withgeojson;
|
||||
|
||||
/* Get all neighbor geohash boxes for our radius search */
|
||||
GeoHashRadius georadius =
|
||||
geohashGetAreasByRadiusWGS84(latlong[0], latlong[1], radius_meters);
|
||||
@ -508,23 +459,17 @@ static void geoRadiusGeneric(redisClient *c, int type) {
|
||||
if (withhash)
|
||||
option_length++;
|
||||
|
||||
if (withgeojson)
|
||||
option_length++;
|
||||
|
||||
if (withgeojsonbounds)
|
||||
option_length++;
|
||||
|
||||
/* The multibulk len we send is exactly result_length. The result is either
|
||||
* all strings of just zset members *or* a nested multi-bulk reply
|
||||
* containing the zset member string _and_ all the additional options the
|
||||
* user enabled for this request. */
|
||||
addReplyMultiBulkLen(c, result_length + withgeojsoncollection);
|
||||
addReplyMultiBulkLen(c, result_length);
|
||||
|
||||
/* Iterate over results, populate struct used for sorting and result sending
|
||||
*/
|
||||
listIter li;
|
||||
listRewind(found_matches, &li);
|
||||
struct geojsonPoint gp[result_length];
|
||||
struct geoPoint gp[result_length];
|
||||
/* populate gp array from our results */
|
||||
for (int i = 0; i < result_length; i++) {
|
||||
struct zipresult *zr = listNodeValue(listNext(&li));
|
||||
@ -534,7 +479,7 @@ static void geoRadiusGeneric(redisClient *c, int type) {
|
||||
gp[i].dist = zr->distance / conversion;
|
||||
gp[i].userdata = zr;
|
||||
|
||||
/* The layout of geojsonPoint allows us to pass the start offset
|
||||
/* The layout of geoPoint allows us to pass the start offset
|
||||
* of the struct directly to decodeGeohash. */
|
||||
decodeGeohash(zr->score, (double *)(gp + i));
|
||||
}
|
||||
@ -558,18 +503,14 @@ static void geoRadiusGeneric(redisClient *c, int type) {
|
||||
switch (zr->type) {
|
||||
case ZR_LONG:
|
||||
addReplyBulkLongLong(c, zr->val.v);
|
||||
if (withgeo && !noproperties)
|
||||
gp[i].member = sdscatprintf(sdsempty(), "%llu", zr->val.v);
|
||||
break;
|
||||
case ZR_STRING:
|
||||
addReplyBulkCBuffer(c, zr->val.s, sdslen(zr->val.s));
|
||||
if (withgeo && !noproperties)
|
||||
gp[i].member = sdsdup(zr->val.s);
|
||||
break;
|
||||
}
|
||||
|
||||
if (withdist)
|
||||
addReplyDoubleNicer(c, gp[i].dist);
|
||||
addReplyDoubleMeters(c, gp[i].dist);
|
||||
|
||||
if (withhash)
|
||||
addReplyLongLong(c, zr->score);
|
||||
@ -579,21 +520,7 @@ static void geoRadiusGeneric(redisClient *c, int type) {
|
||||
addReplyDouble(c, gp[i].latitude);
|
||||
addReplyDouble(c, gp[i].longitude);
|
||||
}
|
||||
|
||||
if (withgeojson)
|
||||
latLongToGeojsonAndReply(c, gp + i, units);
|
||||
|
||||
if (withgeojsonbounds)
|
||||
decodeGeohashToGeojsonBoundsAndReply(c, zr->score, gp + i);
|
||||
}
|
||||
|
||||
if (withgeojsoncollection)
|
||||
replyGeojsonCollection(c, gp, result_length, units);
|
||||
|
||||
if (withgeo && !noproperties)
|
||||
for (int i = 0; i < result_length; i++)
|
||||
sdsfree(gp[i].member);
|
||||
|
||||
listRelease(found_matches);
|
||||
}
|
||||
|
||||
@ -610,18 +537,11 @@ void geoRadiusByMemberCommand(redisClient *c) {
|
||||
}
|
||||
|
||||
void geoDecodeCommand(redisClient *c) {
|
||||
/* args 0-1: ["geodecode", geohash];
|
||||
* optional: [geojson] */
|
||||
|
||||
GeoHashBits geohash;
|
||||
if (getLongLongFromObjectOrReply(c, c->argv[1], (long long *)&geohash.bits,
|
||||
NULL) != REDIS_OK)
|
||||
return;
|
||||
|
||||
int withgeojson = 0;
|
||||
if (c->argc == 3)
|
||||
withgeojson = 1;
|
||||
|
||||
GeoHashArea area;
|
||||
geohash.step = GEO_STEP_MAX;
|
||||
geohashDecodeWGS84(geohash, &area);
|
||||
@ -630,7 +550,7 @@ void geoDecodeCommand(redisClient *c) {
|
||||
double x = (area.longitude.min + area.longitude.max) / 2;
|
||||
|
||||
/* Returning three nested replies */
|
||||
addReplyMultiBulkLen(c, 3 + withgeojson * 2);
|
||||
addReplyMultiBulkLen(c, 3);
|
||||
|
||||
/* First, the minimum corner */
|
||||
addReplyMultiBulkLen(c, 2);
|
||||
@ -646,50 +566,23 @@ void geoDecodeCommand(redisClient *c) {
|
||||
addReplyMultiBulkLen(c, 2);
|
||||
addReplyDouble(c, y);
|
||||
addReplyDouble(c, x);
|
||||
|
||||
if (withgeojson) {
|
||||
struct geojsonPoint gp = { .latitude = y,
|
||||
.longitude = x,
|
||||
.member = NULL };
|
||||
|
||||
/* Return geojson Feature Point */
|
||||
latLongToGeojsonAndReply(c, &gp, NULL);
|
||||
|
||||
/* Return geojson Feature Polygon */
|
||||
decodeGeohashToGeojsonBoundsAndReply(c, geohash.bits, &gp);
|
||||
}
|
||||
}
|
||||
|
||||
void geoEncodeCommand(redisClient *c) {
|
||||
/* args 0-2: ["geoencode", lat, long];
|
||||
* optionals: [radius, units]
|
||||
* - AND / OR -
|
||||
* optional: [geojson] */
|
||||
|
||||
int withgeojson = 0;
|
||||
for (int i = 3; i < c->argc; i++) {
|
||||
char *arg = c->argv[i]->ptr;
|
||||
if (!strncasecmp(arg, "withgeo", 7) || !strcasecmp(arg, "geojson") ||
|
||||
!strcasecmp(arg, "json") || !strcasecmp(arg, "withjson")) {
|
||||
withgeojson = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
* optionals: [radius, units] */
|
||||
|
||||
double radius_meters = 0;
|
||||
if (c->argc >= 5) {
|
||||
if ((radius_meters = extractDistanceOrReply(c, c->argv + 3, NULL)) <
|
||||
0) {
|
||||
if ((radius_meters = extractDistanceOrReply(c, c->argv + 3, NULL)) < 0)
|
||||
return;
|
||||
}
|
||||
} else if (c->argc == 4 && !withgeojson) {
|
||||
} else if (c->argc == 4) {
|
||||
addReplyError(c, "must provide units when asking for radius encode");
|
||||
return;
|
||||
}
|
||||
|
||||
double latlong[2];
|
||||
if (!extractLatLongOrReply(c, c->argv + 1, latlong))
|
||||
return;
|
||||
if (!extractLatLongOrReply(c, c->argv + 1, latlong)) return;
|
||||
|
||||
/* Encode lat/long into our geohash */
|
||||
GeoHashBits geohash;
|
||||
@ -709,8 +602,8 @@ void geoEncodeCommand(redisClient *c) {
|
||||
double y = (area.latitude.min + area.latitude.max) / 2;
|
||||
double x = (area.longitude.min + area.longitude.max) / 2;
|
||||
|
||||
/* Return four nested multibulk replies with optional geojson returns */
|
||||
addReplyMultiBulkLen(c, 4 + withgeojson * 2);
|
||||
/* Return four nested multibulk replies. */
|
||||
addReplyMultiBulkLen(c, 4);
|
||||
|
||||
/* Return the binary geohash we calculated as 52-bit integer */
|
||||
addReplyLongLong(c, bits);
|
||||
@ -729,22 +622,4 @@ void geoEncodeCommand(redisClient *c) {
|
||||
addReplyMultiBulkLen(c, 2);
|
||||
addReplyDouble(c, y);
|
||||
addReplyDouble(c, x);
|
||||
|
||||
if (withgeojson) {
|
||||
struct geojsonPoint gp = { .latitude = y,
|
||||
.longitude = x,
|
||||
.member = NULL };
|
||||
|
||||
/* Return geojson Feature Point */
|
||||
latLongToGeojsonAndReply(c, &gp, NULL);
|
||||
|
||||
/* Return geojson Feature Polygon (bounding box for this step size) */
|
||||
/* We don't use the helper function here because we can't re-calculate
|
||||
* the area if we have a non-GEO_STEP_MAX step size. */
|
||||
sds geojson = geojsonBoxToPolygonFeature(
|
||||
area.latitude.min, area.longitude.min, area.latitude.max,
|
||||
area.longitude.max, gp.set, gp.member);
|
||||
addReplyBulkCBuffer(c, geojson, sdslen(geojson));
|
||||
sdsfree(geojson);
|
||||
}
|
||||
}
|
||||
|
@ -9,4 +9,13 @@ void geoRadiusByMemberCommand(redisClient *c);
|
||||
void geoRadiusCommand(redisClient *c);
|
||||
void geoAddCommand(redisClient *c);
|
||||
|
||||
struct geoPoint {
|
||||
double latitude;
|
||||
double longitude;
|
||||
double dist;
|
||||
char *set;
|
||||
char *member;
|
||||
void *userdata;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
265
src/geojson.c
265
src/geojson.c
@ -1,265 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Matt Stancliff <matt@genges.com>.
|
||||
* 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 Redis 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 OWNER 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 "geojson.h"
|
||||
|
||||
#define L server.lua
|
||||
|
||||
/* ====================================================================
|
||||
* The Encoder
|
||||
* ==================================================================== */
|
||||
static sds jsonEncode() {
|
||||
/* When entering this function, stack is: [1:[geojson table to encode]] */
|
||||
lua_getglobal(L, "cjson");
|
||||
lua_getfield(L, -1, "encode");
|
||||
|
||||
/* Stack is now: [1:[geojson table], 2:'cjson', 3:'encode'] */
|
||||
|
||||
/* Move current top ('encode') to bottom of stack */
|
||||
lua_insert(L, 1);
|
||||
|
||||
/* Move current top ('cjson') to bottom of stack so we can 'cjson.encode' */
|
||||
lua_insert(L, 1);
|
||||
|
||||
/* Stack is now: [1:'cjson', 2:'encode', 3:[table of geojson to encode]] */
|
||||
|
||||
/* Call cjson.encode on the element above it on the stack;
|
||||
* obtain one return value */
|
||||
if (lua_pcall(L, 1, 1, 0) != 0)
|
||||
redisLog(REDIS_WARNING, "Could not encode geojson: %s",
|
||||
lua_tostring(L, -1));
|
||||
|
||||
sds geojson = sdsnew(lua_tostring(L, -1));
|
||||
|
||||
/* We're done. Remove entire stack. Drop mic. Walk away. */
|
||||
lua_pop(L, lua_gettop(L));
|
||||
|
||||
/* Return sds the caller must sdsfree() on their own */
|
||||
return geojson;
|
||||
}
|
||||
|
||||
/* ====================================================================
|
||||
* The Lua Helpers
|
||||
* ==================================================================== */
|
||||
static inline void luaCreateFieldFromPrevious(const char *field) {
|
||||
lua_setfield(L, -2, field);
|
||||
}
|
||||
|
||||
static inline void luaCreateFieldStr(const char *field, const char *value) {
|
||||
lua_pushstring(L, value);
|
||||
luaCreateFieldFromPrevious(field);
|
||||
}
|
||||
|
||||
/* Creates [Lat, Long] array attached to "coordinates" key */
|
||||
static void luaCreateCoordinates(const double x, const double y) {
|
||||
/* Create array table with two elements */
|
||||
lua_createtable(L, 2, 0);
|
||||
|
||||
lua_pushnumber(L, x);
|
||||
lua_rawseti(L, -2, 1);
|
||||
lua_pushnumber(L, y);
|
||||
lua_rawseti(L, -2, 2);
|
||||
}
|
||||
|
||||
static void luaCreatePropertyNull(void) {
|
||||
/* Create empty table and give it a name. This is a json {} value. */
|
||||
lua_createtable(L, 0, 0);
|
||||
luaCreateFieldFromPrevious("properties");
|
||||
}
|
||||
|
||||
static void _luaCreateProperties(const char *k1, const char *v1, const char *k2,
|
||||
const char *v2, const int noclose) {
|
||||
/* we may add additional properties outside of here, so newtable instead of
|
||||
* fixed-size createtable */
|
||||
lua_newtable(L);
|
||||
|
||||
luaCreateFieldStr(k1, v1);
|
||||
luaCreateFieldStr(k2, v2);
|
||||
|
||||
if (!noclose)
|
||||
luaCreateFieldFromPrevious("properties");
|
||||
}
|
||||
|
||||
static void luaCreateProperties(const char *k1, const char *v1, const char *k2,
|
||||
const char *v2) {
|
||||
_luaCreateProperties(k1, v1, k2, v2, 0);
|
||||
}
|
||||
|
||||
/* ====================================================================
|
||||
* The Lua Aggregation Helpers
|
||||
* ==================================================================== */
|
||||
static void attachProperties(const char *set, const char *member) {
|
||||
if (member)
|
||||
luaCreateProperties("set", set, "member", member);
|
||||
else
|
||||
luaCreatePropertyNull();
|
||||
}
|
||||
|
||||
static void attachPropertiesWithDist(const char *set, const char *member,
|
||||
double dist, const char *units) {
|
||||
if (member) {
|
||||
_luaCreateProperties("set", set, "member", member, 1);
|
||||
if (units) {
|
||||
/* Add units then distance. After encoding it comes
|
||||
* out as distance followed by units in the json. */
|
||||
lua_pushstring(L, units);
|
||||
luaCreateFieldFromPrevious("units");
|
||||
lua_pushnumber(L, dist);
|
||||
luaCreateFieldFromPrevious("distance");
|
||||
}
|
||||
|
||||
/* We requested to leave the properties table open, but now we
|
||||
* are done and can close it. */
|
||||
luaCreateFieldFromPrevious("properties");
|
||||
} else {
|
||||
luaCreatePropertyNull();
|
||||
}
|
||||
}
|
||||
|
||||
static void createGeometryPoint(const double x, const double y) {
|
||||
lua_createtable(L, 0, 2);
|
||||
|
||||
/* coordinates = [x, y] */
|
||||
luaCreateCoordinates(x, y);
|
||||
luaCreateFieldFromPrevious("coordinates");
|
||||
|
||||
/* type = Point */
|
||||
luaCreateFieldStr("type", "Point");
|
||||
|
||||
/* geometry = (coordinates = [x, y]) */
|
||||
luaCreateFieldFromPrevious("geometry");
|
||||
}
|
||||
|
||||
static void createGeometryBox(const double x1, const double y1, const double x2,
|
||||
const double y2) {
|
||||
lua_createtable(L, 0, 2);
|
||||
|
||||
/* Result = [[[x1,y1],[x2,y1],[x2,y2],[x1,y2], [x1,y1]] */
|
||||
/* The end coord is the start coord to make a closed polygon */
|
||||
lua_createtable(L, 1, 0);
|
||||
lua_createtable(L, 5, 0);
|
||||
|
||||
/* Bottom left */
|
||||
luaCreateCoordinates(x1, y1);
|
||||
lua_rawseti(L, -2, 1);
|
||||
|
||||
/* Top Left */
|
||||
luaCreateCoordinates(x2, y1);
|
||||
lua_rawseti(L, -2, 2);
|
||||
|
||||
/* Top Right */
|
||||
luaCreateCoordinates(x2, y2);
|
||||
lua_rawseti(L, -2, 3);
|
||||
|
||||
/* Bottom Right */
|
||||
luaCreateCoordinates(x1, y2);
|
||||
lua_rawseti(L, -2, 4);
|
||||
|
||||
/* Bottom Left (Again) */
|
||||
luaCreateCoordinates(x1, y1);
|
||||
lua_rawseti(L, -2, 5);
|
||||
|
||||
/* Set the outer array of our inner array of the inner coords */
|
||||
lua_rawseti(L, -2, 1);
|
||||
|
||||
/* Bundle those together in coordinates: [a, b, c, d] */
|
||||
luaCreateFieldFromPrevious("coordinates");
|
||||
|
||||
/* Add type field */
|
||||
luaCreateFieldStr("type", "Polygon");
|
||||
|
||||
luaCreateFieldFromPrevious("geometry");
|
||||
}
|
||||
|
||||
static void createFeature() {
|
||||
/* Features have three fields: type, geometry, and properties */
|
||||
lua_createtable(L, 0, 3);
|
||||
|
||||
luaCreateFieldStr("type", "Feature");
|
||||
|
||||
/* You must call attachProperties on your own */
|
||||
}
|
||||
|
||||
static void createCollection(size_t size) {
|
||||
/* FeatureCollections have two fields: type and features */
|
||||
lua_createtable(L, 0, 2);
|
||||
|
||||
luaCreateFieldStr("type", "FeatureCollection");
|
||||
}
|
||||
|
||||
static void pointsToCollection(const struct geojsonPoint *pts, const size_t len,
|
||||
const char *units) {
|
||||
createCollection(len);
|
||||
|
||||
lua_createtable(L, len, 0);
|
||||
for (int i = 0; i < len; i++) {
|
||||
createFeature();
|
||||
createGeometryPoint(pts[i].longitude, pts[i].latitude); /* x, y */
|
||||
attachPropertiesWithDist(pts[i].set, pts[i].member, pts[i].dist, units);
|
||||
lua_rawseti(L, -2, i + 1); /* Attach this Feature to "features" array */
|
||||
}
|
||||
luaCreateFieldFromPrevious("features");
|
||||
}
|
||||
|
||||
static void latLongToPointFeature(const double latitude,
|
||||
const double longitude) {
|
||||
createFeature();
|
||||
createGeometryPoint(longitude, latitude); /* geojson is: x,y */
|
||||
}
|
||||
|
||||
static void squareToPolygonFeature(const double x1, const double y1,
|
||||
const double x2, const double y2) {
|
||||
createFeature();
|
||||
createGeometryBox(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
/* ====================================================================
|
||||
* The Interface Functions
|
||||
* ==================================================================== */
|
||||
sds geojsonFeatureCollection(const struct geojsonPoint *pts, const size_t len,
|
||||
const char *units) {
|
||||
pointsToCollection(pts, len, units);
|
||||
return jsonEncode();
|
||||
}
|
||||
|
||||
sds geojsonLatLongToPointFeature(const double latitude, const double longitude,
|
||||
const char *set, const char *member,
|
||||
const double dist, const char *units) {
|
||||
latLongToPointFeature(latitude, longitude);
|
||||
attachPropertiesWithDist(set, member, dist, units);
|
||||
return jsonEncode();
|
||||
}
|
||||
|
||||
sds geojsonBoxToPolygonFeature(const double y1, const double x1,
|
||||
const double y2, const double x2,
|
||||
const char *set, const char *member) {
|
||||
squareToPolygonFeature(x1, y1, x2, y2);
|
||||
attachProperties(set, member);
|
||||
return jsonEncode();
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Matt Stancliff <matt@genges.com>.
|
||||
* 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 Redis 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 OWNER 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.
|
||||
*/
|
||||
|
||||
#ifndef __GEOJSON_H__
|
||||
#define __GEOJSON_H__
|
||||
|
||||
#include "redis.h"
|
||||
#include "geohash_helper.h"
|
||||
|
||||
struct geojsonPoint {
|
||||
double latitude;
|
||||
double longitude;
|
||||
double dist;
|
||||
char *set;
|
||||
char *member;
|
||||
void *userdata;
|
||||
};
|
||||
|
||||
sds geojsonLatLongToPointFeature(const double latitude, const double longitude,
|
||||
const char *set, const char *member,
|
||||
const double dist, const char *units);
|
||||
sds geojsonBoxToPolygonFeature(const double x1, const double y1,
|
||||
const double x2, const double y2,
|
||||
const char *set, const char *member);
|
||||
sds geojsonFeatureCollection(const struct geojsonPoint *pts, const size_t len,
|
||||
const char *units);
|
||||
|
||||
#endif
|
@ -286,7 +286,7 @@ struct redisCommand redisCommandTable[] = {
|
||||
{"georadius",geoRadiusCommand,-6,"r",0,NULL,1,1,1,0,0},
|
||||
{"georadiusbymember",geoRadiusByMemberCommand,-5,"r",0,NULL,1,1,1,0,0},
|
||||
{"geoencode",geoEncodeCommand,-3,"r",0,NULL,0,0,0,0,0},
|
||||
{"geodecode",geoDecodeCommand,-2,"r",0,NULL,0,0,0,0,0},
|
||||
{"geodecode",geoDecodeCommand,2,"r",0,NULL,0,0,0,0,0},
|
||||
{"pfselftest",pfselftestCommand,1,"r",0,NULL,0,0,0,0,0},
|
||||
{"pfadd",pfaddCommand,-2,"wmF",0,NULL,1,1,1,0,0},
|
||||
{"pfcount",pfcountCommand,-2,"r",0,NULL,1,1,1,0,0},
|
||||
|
Loading…
x
Reference in New Issue
Block a user