#include <stdlib.h>
#ifdef GEOPOLY_ENABLE_DEBUG
static int geo_debug = 0;
# define GEODEBUG(X) if(geo_debug)printf X
#else
# define GEODEBUG(X)
#endif
#ifdef sqlite3Isdigit
# define safe_isdigit(x) sqlite3Isdigit(x)
# define safe_isalnum(x) sqlite3Isalnum(x)
# define safe_isxdigit(x) sqlite3Isxdigit(x)
#else
#include <ctype.h>
# define safe_isdigit(x) isdigit((unsigned char)(x))
# define safe_isalnum(x) isalnum((unsigned char)(x))
# define safe_isxdigit(x) isxdigit((unsigned char)(x))
#endif
#ifndef JSON_NULL
static const char geopolyIsSpace[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
#define fast_isspace(x) (geopolyIsSpace[(unsigned char)x])
#endif
#ifndef GCC_VERSION
#if defined(__GNUC__) && !defined(SQLITE_DISABLE_INTRINSIC)
# define GCC_VERSION (__GNUC__*1000000+__GNUC_MINOR__*1000+__GNUC_PATCHLEVEL__)
#else
# define GCC_VERSION 0
#endif
#endif
#ifndef MSVC_VERSION
#if defined(_MSC_VER) && !defined(SQLITE_DISABLE_INTRINSIC)
# define MSVC_VERSION _MSC_VER
#else
# define MSVC_VERSION 0
#endif
#endif
typedef float GeoCoord;
typedef struct GeoPoly GeoPoly;
struct GeoPoly {
int nVertex;
unsigned char hdr[4];
GeoCoord a[8];
};
#define GEOPOLY_SZ(N) (sizeof(GeoPoly) + sizeof(GeoCoord)*2*((N)-4))
#define GeoX(P,I) (((GeoCoord*)(P)->a)[(I)*2])
#define GeoY(P,I) (((GeoCoord*)(P)->a)[(I)*2+1])
typedef struct GeoParse GeoParse;
struct GeoParse {
const unsigned char *z;
int nVertex;
int nAlloc;
int nErr;
GeoCoord *a;
};
static void geopolySwab32(unsigned char *a){
unsigned char t = a[0];
a[0] = a[3];
a[3] = t;
t = a[1];
a[1] = a[2];
a[2] = t;
}
static char geopolySkipSpace(GeoParse *p){
while( fast_isspace(p->z[0]) ) p->z++;
return p->z[0];
}
static int geopolyParseNumber(GeoParse *p, GeoCoord *pVal){
char c = geopolySkipSpace(p);
const unsigned char *z = p->z;
int j = 0;
int seenDP = 0;
int seenE = 0;
if( c=='-' ){
j = 1;
c = z[j];
}
if( c=='0' && z[j+1]>='0' && z[j+1]<='9' ) return 0;
for(;; j++){
c = z[j];
if( safe_isdigit(c) ) continue;
if( c=='.' ){
if( z[j-1]=='-' ) return 0;
if( seenDP ) return 0;
seenDP = 1;
continue;
}
if( c=='e' || c=='E' ){
if( z[j-1]<'0' ) return 0;
if( seenE ) return -1;
seenDP = seenE = 1;
c = z[j+1];
if( c=='+' || c=='-' ){
j++;
c = z[j+1];
}
if( c<'0' || c>'9' ) return 0;
continue;
}
break;
}
if( z[j-1]<'0' ) return 0;
if( pVal ){
#ifdef SQLITE_AMALGAMATION
double r;
(void)sqlite3AtoF((const char*)p->z, &r, j, SQLITE_UTF8);
*pVal = r;
#else
*pVal = (GeoCoord)atof((const char*)p->z);
#endif
}
p->z += j;
return 1;
}
static GeoPoly *geopolyParseJson(const unsigned char *z, int *pRc){
GeoParse s;
int rc = SQLITE_OK;
memset(&s, 0, sizeof(s));
s.z = z;
if( geopolySkipSpace(&s)=='[' ){
s.z++;
while( geopolySkipSpace(&s)=='[' ){
int ii = 0;
char c;
s.z++;
if( s.nVertex>=s.nAlloc ){
GeoCoord *aNew;
s.nAlloc = s.nAlloc*2 + 16;
aNew = sqlite3_realloc64(s.a, s.nAlloc*sizeof(GeoCoord)*2 );
if( aNew==0 ){
rc = SQLITE_NOMEM;
s.nErr++;
break;
}
s.a = aNew;
}
while( geopolyParseNumber(&s, ii<=1 ? &s.a[s.nVertex*2+ii] : 0) ){
ii++;
if( ii==2 ) s.nVertex++;
c = geopolySkipSpace(&s);
s.z++;
if( c==',' ) continue;
if( c==']' && ii>=2 ) break;
s.nErr++;
rc = SQLITE_ERROR;
goto parse_json_err;
}
if( geopolySkipSpace(&s)==',' ){
s.z++;
continue;
}
break;
}
if( geopolySkipSpace(&s)==']'
&& s.nVertex>=4
&& s.a[0]==s.a[s.nVertex*2-2]
&& s.a[1]==s.a[s.nVertex*2-1]
&& (s.z++, geopolySkipSpace(&s)==0)
){
GeoPoly *pOut;
int x = 1;
s.nVertex--;
pOut = sqlite3_malloc64( GEOPOLY_SZ((sqlite3_int64)s.nVertex) );
x = 1;
if( pOut==0 ) goto parse_json_err;
pOut->nVertex = s.nVertex;
memcpy(pOut->a, s.a, s.nVertex*2*sizeof(GeoCoord));
pOut->hdr[0] = *(unsigned char*)&x;
pOut->hdr[1] = (s.nVertex>>16)&0xff;
pOut->hdr[2] = (s.nVertex>>8)&0xff;
pOut->hdr[3] = s.nVertex&0xff;
sqlite3_free(s.a);
if( pRc ) *pRc = SQLITE_OK;
return pOut;
}else{
s.nErr++;
rc = SQLITE_ERROR;
}
}
parse_json_err:
if( pRc ) *pRc = rc;
sqlite3_free(s.a);
return 0;
}
static GeoPoly *geopolyFuncParam(
sqlite3_context *pCtx,
sqlite3_value *pVal,
int *pRc
){
GeoPoly *p = 0;
int nByte;
testcase( pCtx==0 );
if( sqlite3_value_type(pVal)==SQLITE_BLOB
&& (nByte = sqlite3_value_bytes(pVal))>=(int)(4+6*sizeof(GeoCoord))
){
const unsigned char *a = sqlite3_value_blob(pVal);
int nVertex;
if( a==0 ){
if( pCtx ) sqlite3_result_error_nomem(pCtx);
return 0;
}
nVertex = (a[1]<<16) + (a[2]<<8) + a[3];
if( (a[0]==0 || a[0]==1)
&& (nVertex*2*sizeof(GeoCoord) + 4)==(unsigned int)nByte
){
p = sqlite3_malloc64( sizeof(*p) + (nVertex-1)*2*sizeof(GeoCoord) );
if( p==0 ){
if( pRc ) *pRc = SQLITE_NOMEM;
if( pCtx ) sqlite3_result_error_nomem(pCtx);
}else{
int x = 1;
p->nVertex = nVertex;
memcpy(p->hdr, a, nByte);
if( a[0] != *(unsigned char*)&x ){
int ii;
for(ii=0; ii<nVertex; ii++){
geopolySwab32((unsigned char*)&GeoX(p,ii));
geopolySwab32((unsigned char*)&GeoY(p,ii));
}
p->hdr[0] ^= 1;
}
}
}
if( pRc ) *pRc = SQLITE_OK;
return p;
}else if( sqlite3_value_type(pVal)==SQLITE_TEXT ){
const unsigned char *zJson = sqlite3_value_text(pVal);
if( zJson==0 ){
if( pRc ) *pRc = SQLITE_NOMEM;
return 0;
}
return geopolyParseJson(zJson, pRc);
}else{
if( pRc ) *pRc = SQLITE_ERROR;
return 0;
}
}
static void geopolyBlobFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
(void)argc;
if( p ){
sqlite3_result_blob(context, p->hdr,
4+8*p->nVertex, SQLITE_TRANSIENT);
sqlite3_free(p);
}
}
static void geopolyJsonFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
(void)argc;
if( p ){
sqlite3 *db = sqlite3_context_db_handle(context);
sqlite3_str *x = sqlite3_str_new(db);
int i;
sqlite3_str_append(x, "[", 1);
for(i=0; i<p->nVertex; i++){
sqlite3_str_appendf(x, "[%!g,%!g],", GeoX(p,i), GeoY(p,i));
}
sqlite3_str_appendf(x, "[%!g,%!g]]", GeoX(p,0), GeoY(p,0));
sqlite3_result_text(context, sqlite3_str_finish(x), -1, sqlite3_free);
sqlite3_free(p);
}
}
static void geopolySvgFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
GeoPoly *p;
if( argc<1 ) return;
p = geopolyFuncParam(context, argv[0], 0);
if( p ){
sqlite3 *db = sqlite3_context_db_handle(context);
sqlite3_str *x = sqlite3_str_new(db);
int i;
char cSep = '\'';
sqlite3_str_appendf(x, "<polyline points=");
for(i=0; i<p->nVertex; i++){
sqlite3_str_appendf(x, "%c%g,%g", cSep, GeoX(p,i), GeoY(p,i));
cSep = ' ';
}
sqlite3_str_appendf(x, " %g,%g'", GeoX(p,0), GeoY(p,0));
for(i=1; i<argc; i++){
const char *z = (const char*)sqlite3_value_text(argv[i]);
if( z && z[0] ){
sqlite3_str_appendf(x, " %s", z);
}
}
sqlite3_str_appendf(x, "></polyline>");
sqlite3_result_text(context, sqlite3_str_finish(x), -1, sqlite3_free);
sqlite3_free(p);
}
}
static void geopolyXformFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
double A = sqlite3_value_double(argv[1]);
double B = sqlite3_value_double(argv[2]);
double C = sqlite3_value_double(argv[3]);
double D = sqlite3_value_double(argv[4]);
double E = sqlite3_value_double(argv[5]);
double F = sqlite3_value_double(argv[6]);
GeoCoord x1, y1, x0, y0;
int ii;
(void)argc;
if( p ){
for(ii=0; ii<p->nVertex; ii++){
x0 = GeoX(p,ii);
y0 = GeoY(p,ii);
x1 = (GeoCoord)(A*x0 + B*y0 + E);
y1 = (GeoCoord)(C*x0 + D*y0 + F);
GeoX(p,ii) = x1;
GeoY(p,ii) = y1;
}
sqlite3_result_blob(context, p->hdr,
4+8*p->nVertex, SQLITE_TRANSIENT);
sqlite3_free(p);
}
}
static double geopolyArea(GeoPoly *p){
double rArea = 0.0;
int ii;
for(ii=0; ii<p->nVertex-1; ii++){
rArea += (GeoX(p,ii) - GeoX(p,ii+1))
* (GeoY(p,ii) + GeoY(p,ii+1))
* 0.5;
}
rArea += (GeoX(p,ii) - GeoX(p,0))
* (GeoY(p,ii) + GeoY(p,0))
* 0.5;
return rArea;
}
static void geopolyAreaFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
(void)argc;
if( p ){
sqlite3_result_double(context, geopolyArea(p));
sqlite3_free(p);
}
}
static void geopolyCcwFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
(void)argc;
if( p ){
if( geopolyArea(p)<0.0 ){
int ii, jj;
for(ii=1, jj=p->nVertex-1; ii<jj; ii++, jj--){
GeoCoord t = GeoX(p,ii);
GeoX(p,ii) = GeoX(p,jj);
GeoX(p,jj) = t;
t = GeoY(p,ii);
GeoY(p,ii) = GeoY(p,jj);
GeoY(p,jj) = t;
}
}
sqlite3_result_blob(context, p->hdr,
4+8*p->nVertex, SQLITE_TRANSIENT);
sqlite3_free(p);
}
}
#define GEOPOLY_PI 3.1415926535897932385
static double geopolySine(double r){
assert( r>=-0.5*GEOPOLY_PI && r<=2.0*GEOPOLY_PI );
if( r>=1.5*GEOPOLY_PI ){
r -= 2.0*GEOPOLY_PI;
}
if( r>=0.5*GEOPOLY_PI ){
return -geopolySine(r-GEOPOLY_PI);
}else{
double r2 = r*r;
double r3 = r2*r;
double r5 = r3*r2;
return 0.9996949*r - 0.1656700*r3 + 0.0075134*r5;
}
}
static void geopolyRegularFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
double x = sqlite3_value_double(argv[0]);
double y = sqlite3_value_double(argv[1]);
double r = sqlite3_value_double(argv[2]);
int n = sqlite3_value_int(argv[3]);
int i;
GeoPoly *p;
(void)argc;
if( n<3 || r<=0.0 ) return;
if( n>1000 ) n = 1000;
p = sqlite3_malloc64( sizeof(*p) + (n-1)*2*sizeof(GeoCoord) );
if( p==0 ){
sqlite3_result_error_nomem(context);
return;
}
i = 1;
p->hdr[0] = *(unsigned char*)&i;
p->hdr[1] = 0;
p->hdr[2] = (n>>8)&0xff;
p->hdr[3] = n&0xff;
for(i=0; i<n; i++){
double rAngle = 2.0*GEOPOLY_PI*i/n;
GeoX(p,i) = x - r*geopolySine(rAngle-0.5*GEOPOLY_PI);
GeoY(p,i) = y + r*geopolySine(rAngle);
}
sqlite3_result_blob(context, p->hdr, 4+8*n, SQLITE_TRANSIENT);
sqlite3_free(p);
}
static GeoPoly *geopolyBBox(
sqlite3_context *context,
sqlite3_value *pPoly,
RtreeCoord *aCoord,
int *pRc
){
GeoPoly *pOut = 0;
GeoPoly *p;
float mnX, mxX, mnY, mxY;
if( pPoly==0 && aCoord!=0 ){
p = 0;
mnX = aCoord[0].f;
mxX = aCoord[1].f;
mnY = aCoord[2].f;
mxY = aCoord[3].f;
goto geopolyBboxFill;
}else{
p = geopolyFuncParam(context, pPoly, pRc);
}
if( p ){
int ii;
mnX = mxX = GeoX(p,0);
mnY = mxY = GeoY(p,0);
for(ii=1; ii<p->nVertex; ii++){
double r = GeoX(p,ii);
if( r<mnX ) mnX = (float)r;
else if( r>mxX ) mxX = (float)r;
r = GeoY(p,ii);
if( r<mnY ) mnY = (float)r;
else if( r>mxY ) mxY = (float)r;
}
if( pRc ) *pRc = SQLITE_OK;
if( aCoord==0 ){
geopolyBboxFill:
pOut = sqlite3_realloc64(p, GEOPOLY_SZ(4));
if( pOut==0 ){
sqlite3_free(p);
if( context ) sqlite3_result_error_nomem(context);
if( pRc ) *pRc = SQLITE_NOMEM;
return 0;
}
pOut->nVertex = 4;
ii = 1;
pOut->hdr[0] = *(unsigned char*)ⅈ
pOut->hdr[1] = 0;
pOut->hdr[2] = 0;
pOut->hdr[3] = 4;
GeoX(pOut,0) = mnX;
GeoY(pOut,0) = mnY;
GeoX(pOut,1) = mxX;
GeoY(pOut,1) = mnY;
GeoX(pOut,2) = mxX;
GeoY(pOut,2) = mxY;
GeoX(pOut,3) = mnX;
GeoY(pOut,3) = mxY;
}else{
sqlite3_free(p);
aCoord[0].f = mnX;
aCoord[1].f = mxX;
aCoord[2].f = mnY;
aCoord[3].f = mxY;
}
}else if( aCoord ){
memset(aCoord, 0, sizeof(RtreeCoord)*4);
}
return pOut;
}
static void geopolyBBoxFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
GeoPoly *p = geopolyBBox(context, argv[0], 0, 0);
(void)argc;
if( p ){
sqlite3_result_blob(context, p->hdr,
4+8*p->nVertex, SQLITE_TRANSIENT);
sqlite3_free(p);
}
}
typedef struct GeoBBox GeoBBox;
struct GeoBBox {
int isInit;
RtreeCoord a[4];
};
static void geopolyBBoxStep(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
RtreeCoord a[4];
int rc = SQLITE_OK;
(void)argc;
(void)geopolyBBox(context, argv[0], a, &rc);
if( rc==SQLITE_OK ){
GeoBBox *pBBox;
pBBox = (GeoBBox*)sqlite3_aggregate_context(context, sizeof(*pBBox));
if( pBBox==0 ) return;
if( pBBox->isInit==0 ){
pBBox->isInit = 1;
memcpy(pBBox->a, a, sizeof(RtreeCoord)*4);
}else{
if( a[0].f < pBBox->a[0].f ) pBBox->a[0] = a[0];
if( a[1].f > pBBox->a[1].f ) pBBox->a[1] = a[1];
if( a[2].f < pBBox->a[2].f ) pBBox->a[2] = a[2];
if( a[3].f > pBBox->a[3].f ) pBBox->a[3] = a[3];
}
}
}
static void geopolyBBoxFinal(
sqlite3_context *context
){
GeoPoly *p;
GeoBBox *pBBox;
pBBox = (GeoBBox*)sqlite3_aggregate_context(context, 0);
if( pBBox==0 ) return;
p = geopolyBBox(context, 0, pBBox->a, 0);
if( p ){
sqlite3_result_blob(context, p->hdr,
4+8*p->nVertex, SQLITE_TRANSIENT);
sqlite3_free(p);
}
}
static int pointBeneathLine(
double x0, double y0,
double x1, double y1,
double x2, double y2
){
double y;
if( x0==x1 && y0==y1 ) return 2;
if( x1<x2 ){
if( x0<=x1 || x0>x2 ) return 0;
}else if( x1>x2 ){
if( x0<=x2 || x0>x1 ) return 0;
}else{
if( x0!=x1 ) return 0;
if( y0<y1 && y0<y2 ) return 0;
if( y0>y1 && y0>y2 ) return 0;
return 2;
}
y = y1 + (y2-y1)*(x0-x1)/(x2-x1);
if( y0==y ) return 2;
if( y0<y ) return 1;
return 0;
}
static void geopolyContainsPointFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
GeoPoly *p1 = geopolyFuncParam(context, argv[0], 0);
double x0 = sqlite3_value_double(argv[1]);
double y0 = sqlite3_value_double(argv[2]);
int v = 0;
int cnt = 0;
int ii;
(void)argc;
if( p1==0 ) return;
for(ii=0; ii<p1->nVertex-1; ii++){
v = pointBeneathLine(x0,y0,GeoX(p1,ii), GeoY(p1,ii),
GeoX(p1,ii+1),GeoY(p1,ii+1));
if( v==2 ) break;
cnt += v;
}
if( v!=2 ){
v = pointBeneathLine(x0,y0,GeoX(p1,ii), GeoY(p1,ii),
GeoX(p1,0), GeoY(p1,0));
}
if( v==2 ){
sqlite3_result_int(context, 1);
}else if( ((v+cnt)&1)==0 ){
sqlite3_result_int(context, 0);
}else{
sqlite3_result_int(context, 2);
}
sqlite3_free(p1);
}
static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2);
static void geopolyWithinFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
GeoPoly *p1 = geopolyFuncParam(context, argv[0], 0);
GeoPoly *p2 = geopolyFuncParam(context, argv[1], 0);
(void)argc;
if( p1 && p2 ){
int x = geopolyOverlap(p1, p2);
if( x<0 ){
sqlite3_result_error_nomem(context);
}else{
sqlite3_result_int(context, x==2 ? 1 : x==4 ? 2 : 0);
}
}
sqlite3_free(p1);
sqlite3_free(p2);
}
typedef struct GeoEvent GeoEvent;
typedef struct GeoSegment GeoSegment;
typedef struct GeoOverlap GeoOverlap;
struct GeoEvent {
double x;
int eType;
GeoSegment *pSeg;
GeoEvent *pNext;
};
struct GeoSegment {
double C, B;
double y;
float y0;
unsigned char side;
unsigned int idx;
GeoSegment *pNext;
};
struct GeoOverlap {
GeoEvent *aEvent;
GeoSegment *aSegment;
int nEvent;
int nSegment;
};
static void geopolyAddOneSegment(
GeoOverlap *p,
GeoCoord x0,
GeoCoord y0,
GeoCoord x1,
GeoCoord y1,
unsigned char side,
unsigned int idx
){
GeoSegment *pSeg;
GeoEvent *pEvent;
if( x0==x1 ) return;
if( x0>x1 ){
GeoCoord t = x0;
x0 = x1;
x1 = t;
t = y0;
y0 = y1;
y1 = t;
}
pSeg = p->aSegment + p->nSegment;
p->nSegment++;
pSeg->C = (y1-y0)/(x1-x0);
pSeg->B = y1 - x1*pSeg->C;
pSeg->y0 = y0;
pSeg->side = side;
pSeg->idx = idx;
pEvent = p->aEvent + p->nEvent;
p->nEvent++;
pEvent->x = x0;
pEvent->eType = 0;
pEvent->pSeg = pSeg;
pEvent = p->aEvent + p->nEvent;
p->nEvent++;
pEvent->x = x1;
pEvent->eType = 1;
pEvent->pSeg = pSeg;
}
static void geopolyAddSegments(
GeoOverlap *p,
GeoPoly *pPoly,
unsigned char side
){
unsigned int i;
GeoCoord *x;
for(i=0; i<(unsigned)pPoly->nVertex-1; i++){
x = &GeoX(pPoly,i);
geopolyAddOneSegment(p, x[0], x[1], x[2], x[3], side, i);
}
x = &GeoX(pPoly,i);
geopolyAddOneSegment(p, x[0], x[1], pPoly->a[0], pPoly->a[1], side, i);
}
static GeoEvent *geopolyEventMerge(GeoEvent *pLeft, GeoEvent *pRight){
GeoEvent head, *pLast;
head.pNext = 0;
pLast = &head;
while( pRight && pLeft ){
if( pRight->x <= pLeft->x ){
pLast->pNext = pRight;
pLast = pRight;
pRight = pRight->pNext;
}else{
pLast->pNext = pLeft;
pLast = pLeft;
pLeft = pLeft->pNext;
}
}
pLast->pNext = pRight ? pRight : pLeft;
return head.pNext;
}
static GeoEvent *geopolySortEventsByX(GeoEvent *aEvent, int nEvent){
int mx = 0;
int i, j;
GeoEvent *p;
GeoEvent *a[50];
for(i=0; i<nEvent; i++){
p = &aEvent[i];
p->pNext = 0;
for(j=0; j<mx && a[j]; j++){
p = geopolyEventMerge(a[j], p);
a[j] = 0;
}
a[j] = p;
if( j>=mx ) mx = j+1;
}
p = 0;
for(i=0; i<mx; i++){
p = geopolyEventMerge(a[i], p);
}
return p;
}
static GeoSegment *geopolySegmentMerge(GeoSegment *pLeft, GeoSegment *pRight){
GeoSegment head, *pLast;
head.pNext = 0;
pLast = &head;
while( pRight && pLeft ){
double r = pRight->y - pLeft->y;
if( r==0.0 ) r = pRight->C - pLeft->C;
if( r<0.0 ){
pLast->pNext = pRight;
pLast = pRight;
pRight = pRight->pNext;
}else{
pLast->pNext = pLeft;
pLast = pLeft;
pLeft = pLeft->pNext;
}
}
pLast->pNext = pRight ? pRight : pLeft;
return head.pNext;
}
static GeoSegment *geopolySortSegmentsByYAndC(GeoSegment *pList){
int mx = 0;
int i;
GeoSegment *p;
GeoSegment *a[50];
while( pList ){
p = pList;
pList = pList->pNext;
p->pNext = 0;
for(i=0; i<mx && a[i]; i++){
p = geopolySegmentMerge(a[i], p);
a[i] = 0;
}
a[i] = p;
if( i>=mx ) mx = i+1;
}
p = 0;
for(i=0; i<mx; i++){
p = geopolySegmentMerge(a[i], p);
}
return p;
}
static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){
sqlite3_int64 nVertex = p1->nVertex + p2->nVertex + 2;
GeoOverlap *p;
sqlite3_int64 nByte;
GeoEvent *pThisEvent;
double rX;
int rc = 0;
int needSort = 0;
GeoSegment *pActive = 0;
GeoSegment *pSeg;
unsigned char aOverlap[4];
nByte = sizeof(GeoEvent)*nVertex*2
+ sizeof(GeoSegment)*nVertex
+ sizeof(GeoOverlap);
p = sqlite3_malloc64( nByte );
if( p==0 ) return -1;
p->aEvent = (GeoEvent*)&p[1];
p->aSegment = (GeoSegment*)&p->aEvent[nVertex*2];
p->nEvent = p->nSegment = 0;
geopolyAddSegments(p, p1, 1);
geopolyAddSegments(p, p2, 2);
pThisEvent = geopolySortEventsByX(p->aEvent, p->nEvent);
rX = pThisEvent && pThisEvent->x==0.0 ? -1.0 : 0.0;
memset(aOverlap, 0, sizeof(aOverlap));
while( pThisEvent ){
if( pThisEvent->x!=rX ){
GeoSegment *pPrev = 0;
int iMask = 0;
GEODEBUG(("Distinct X: %g\n", pThisEvent->x));
rX = pThisEvent->x;
if( needSort ){
GEODEBUG(("SORT\n"));
pActive = geopolySortSegmentsByYAndC(pActive);
needSort = 0;
}
for(pSeg=pActive; pSeg; pSeg=pSeg->pNext){
if( pPrev ){
if( pPrev->y!=pSeg->y ){
GEODEBUG(("MASK: %d\n", iMask));
aOverlap[iMask] = 1;
}
}
iMask ^= pSeg->side;
pPrev = pSeg;
}
pPrev = 0;
for(pSeg=pActive; pSeg; pSeg=pSeg->pNext){
double y = pSeg->C*rX + pSeg->B;
GEODEBUG(("Segment %d.%d %g->%g\n", pSeg->side, pSeg->idx, pSeg->y, y));
pSeg->y = y;
if( pPrev ){
if( pPrev->y>pSeg->y && pPrev->side!=pSeg->side ){
rc = 1;
GEODEBUG(("Crossing: %d.%d and %d.%d\n",
pPrev->side, pPrev->idx,
pSeg->side, pSeg->idx));
goto geopolyOverlapDone;
}else if( pPrev->y!=pSeg->y ){
GEODEBUG(("MASK: %d\n", iMask));
aOverlap[iMask] = 1;
}
}
iMask ^= pSeg->side;
pPrev = pSeg;
}
}
GEODEBUG(("%s %d.%d C=%g B=%g\n",
pThisEvent->eType ? "RM " : "ADD",
pThisEvent->pSeg->side, pThisEvent->pSeg->idx,
pThisEvent->pSeg->C,
pThisEvent->pSeg->B));
if( pThisEvent->eType==0 ){
pSeg = pThisEvent->pSeg;
pSeg->y = pSeg->y0;
pSeg->pNext = pActive;
pActive = pSeg;
needSort = 1;
}else{
if( pActive==pThisEvent->pSeg ){
pActive = ALWAYS(pActive) ? pActive->pNext : 0;
}else{
for(pSeg=pActive; pSeg; pSeg=pSeg->pNext){
if( pSeg->pNext==pThisEvent->pSeg ){
pSeg->pNext = ALWAYS(pSeg->pNext) ? pSeg->pNext->pNext : 0;
break;
}
}
}
}
pThisEvent = pThisEvent->pNext;
}
if( aOverlap[3]==0 ){
rc = 0;
}else if( aOverlap[1]!=0 && aOverlap[2]==0 ){
rc = 3;
}else if( aOverlap[1]==0 && aOverlap[2]!=0 ){
rc = 2;
}else if( aOverlap[1]==0 && aOverlap[2]==0 ){
rc = 4;
}else{
rc = 1;
}
geopolyOverlapDone:
sqlite3_free(p);
return rc;
}
static void geopolyOverlapFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
GeoPoly *p1 = geopolyFuncParam(context, argv[0], 0);
GeoPoly *p2 = geopolyFuncParam(context, argv[1], 0);
(void)argc;
if( p1 && p2 ){
int x = geopolyOverlap(p1, p2);
if( x<0 ){
sqlite3_result_error_nomem(context);
}else{
sqlite3_result_int(context, x);
}
}
sqlite3_free(p1);
sqlite3_free(p2);
}
static void geopolyDebugFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
(void)context;
(void)argc;
#ifdef GEOPOLY_ENABLE_DEBUG
geo_debug = sqlite3_value_int(argv[0]);
#else
(void)argv;
#endif
}
static int geopolyInit(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr,
int isCreate
){
int rc = SQLITE_OK;
Rtree *pRtree;
sqlite3_int64 nDb;
sqlite3_int64 nName;
sqlite3_str *pSql;
char *zSql;
int ii;
(void)pAux;
sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
nDb = strlen(argv[1]);
nName = strlen(argv[2]);
pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName+2);
if( !pRtree ){
return SQLITE_NOMEM;
}
memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2);
pRtree->nBusy = 1;
pRtree->base.pModule = &rtreeModule;
pRtree->zDb = (char *)&pRtree[1];
pRtree->zName = &pRtree->zDb[nDb+1];
pRtree->eCoordType = RTREE_COORD_REAL32;
pRtree->nDim = 2;
pRtree->nDim2 = 4;
memcpy(pRtree->zDb, argv[1], nDb);
memcpy(pRtree->zName, argv[2], nName);
pSql = sqlite3_str_new(db);
sqlite3_str_appendf(pSql, "CREATE TABLE x(_shape");
pRtree->nAux = 1;
pRtree->nAuxNotNull = 1;
for(ii=3; ii<argc; ii++){
pRtree->nAux++;
sqlite3_str_appendf(pSql, ",%s", argv[ii]);
}
sqlite3_str_appendf(pSql, ");");
zSql = sqlite3_str_finish(pSql);
if( !zSql ){
rc = SQLITE_NOMEM;
}else if( SQLITE_OK!=(rc = sqlite3_declare_vtab(db, zSql)) ){
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
}
sqlite3_free(zSql);
if( rc ) goto geopolyInit_fail;
pRtree->nBytesPerCell = 8 + pRtree->nDim2*4;
rc = getNodeSize(db, pRtree, isCreate, pzErr);
if( rc ) goto geopolyInit_fail;
rc = rtreeSqlInit(pRtree, db, argv[1], argv[2], isCreate);
if( rc ){
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
goto geopolyInit_fail;
}
*ppVtab = (sqlite3_vtab *)pRtree;
return SQLITE_OK;
geopolyInit_fail:
if( rc==SQLITE_OK ) rc = SQLITE_ERROR;
assert( *ppVtab==0 );
assert( pRtree->nBusy==1 );
rtreeRelease(pRtree);
return rc;
}
static int geopolyCreate(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
return geopolyInit(db, pAux, argc, argv, ppVtab, pzErr, 1);
}
static int geopolyConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
return geopolyInit(db, pAux, argc, argv, ppVtab, pzErr, 0);
}
static int geopolyFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum,
const char *idxStr,
int argc, sqlite3_value **argv
){
Rtree *pRtree = (Rtree *)pVtabCursor->pVtab;
RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
RtreeNode *pRoot = 0;
int rc = SQLITE_OK;
int iCell = 0;
(void)idxStr;
rtreeReference(pRtree);
resetCursor(pCsr);
pCsr->iStrategy = idxNum;
if( idxNum==1 ){
RtreeNode *pLeaf;
RtreeSearchPoint *p;
i64 iRowid = sqlite3_value_int64(argv[0]);
i64 iNode = 0;
rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
if( rc==SQLITE_OK && pLeaf!=0 ){
p = rtreeSearchPointNew(pCsr, RTREE_ZERO, 0);
assert( p!=0 );
pCsr->aNode[0] = pLeaf;
p->id = iNode;
p->eWithin = PARTLY_WITHIN;
rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &iCell);
p->iCell = (u8)iCell;
RTREE_QUEUE_TRACE(pCsr, "PUSH-F1:");
}else{
pCsr->atEOF = 1;
}
}else{
rc = nodeAcquire(pRtree, 1, 0, &pRoot);
if( rc==SQLITE_OK && idxNum<=3 ){
RtreeCoord bbox[4];
RtreeConstraint *p;
assert( argc==1 );
assert( argv[0]!=0 );
geopolyBBox(0, argv[0], bbox, &rc);
if( rc ){
goto geopoly_filter_end;
}
pCsr->aConstraint = p = sqlite3_malloc(sizeof(RtreeConstraint)*4);
pCsr->nConstraint = 4;
if( p==0 ){
rc = SQLITE_NOMEM;
}else{
memset(pCsr->aConstraint, 0, sizeof(RtreeConstraint)*4);
memset(pCsr->anQueue, 0, sizeof(u32)*(pRtree->iDepth + 1));
if( idxNum==2 ){
p->op = 'B';
p->iCoord = 0;
p->u.rValue = bbox[1].f;
p++;
p->op = 'D';
p->iCoord = 1;
p->u.rValue = bbox[0].f;
p++;
p->op = 'B';
p->iCoord = 2;
p->u.rValue = bbox[3].f;
p++;
p->op = 'D';
p->iCoord = 3;
p->u.rValue = bbox[2].f;
}else{
p->op = 'D';
p->iCoord = 0;
p->u.rValue = bbox[0].f;
p++;
p->op = 'B';
p->iCoord = 1;
p->u.rValue = bbox[1].f;
p++;
p->op = 'D';
p->iCoord = 2;
p->u.rValue = bbox[2].f;
p++;
p->op = 'B';
p->iCoord = 3;
p->u.rValue = bbox[3].f;
}
}
}
if( rc==SQLITE_OK ){
RtreeSearchPoint *pNew;
pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, (u8)(pRtree->iDepth+1));
if( pNew==0 ){
rc = SQLITE_NOMEM;
goto geopoly_filter_end;
}
pNew->id = 1;
pNew->iCell = 0;
pNew->eWithin = PARTLY_WITHIN;
assert( pCsr->bPoint==1 );
pCsr->aNode[0] = pRoot;
pRoot = 0;
RTREE_QUEUE_TRACE(pCsr, "PUSH-Fm:");
rc = rtreeStepToLeaf(pCsr);
}
}
geopoly_filter_end:
nodeRelease(pRtree, pRoot);
rtreeRelease(pRtree);
return rc;
}
static int geopolyBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int ii;
int iRowidTerm = -1;
int iFuncTerm = -1;
int idxNum = 0;
(void)tab;
for(ii=0; ii<pIdxInfo->nConstraint; ii++){
struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii];
if( !p->usable ) continue;
if( p->iColumn<0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
iRowidTerm = ii;
break;
}
if( p->iColumn==0 && p->op>=SQLITE_INDEX_CONSTRAINT_FUNCTION ){
iFuncTerm = ii;
idxNum = p->op - SQLITE_INDEX_CONSTRAINT_FUNCTION + 2;
}
}
if( iRowidTerm>=0 ){
pIdxInfo->idxNum = 1;
pIdxInfo->idxStr = "rowid";
pIdxInfo->aConstraintUsage[iRowidTerm].argvIndex = 1;
pIdxInfo->aConstraintUsage[iRowidTerm].omit = 1;
pIdxInfo->estimatedCost = 30.0;
pIdxInfo->estimatedRows = 1;
pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE;
return SQLITE_OK;
}
if( iFuncTerm>=0 ){
pIdxInfo->idxNum = idxNum;
pIdxInfo->idxStr = "rtree";
pIdxInfo->aConstraintUsage[iFuncTerm].argvIndex = 1;
pIdxInfo->aConstraintUsage[iFuncTerm].omit = 0;
pIdxInfo->estimatedCost = 300.0;
pIdxInfo->estimatedRows = 10;
return SQLITE_OK;
}
pIdxInfo->idxNum = 4;
pIdxInfo->idxStr = "fullscan";
pIdxInfo->estimatedCost = 3000000.0;
pIdxInfo->estimatedRows = 100000;
return SQLITE_OK;
}
static int geopolyColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
Rtree *pRtree = (Rtree *)cur->pVtab;
RtreeCursor *pCsr = (RtreeCursor *)cur;
RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr);
int rc = SQLITE_OK;
RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc);
if( rc ) return rc;
if( p==0 ) return SQLITE_OK;
if( i==0 && sqlite3_vtab_nochange(ctx) ) return SQLITE_OK;
if( i<=pRtree->nAux ){
if( !pCsr->bAuxValid ){
if( pCsr->pReadAux==0 ){
rc = sqlite3_prepare_v3(pRtree->db, pRtree->zReadAuxSql, -1, 0,
&pCsr->pReadAux, 0);
if( rc ) return rc;
}
sqlite3_bind_int64(pCsr->pReadAux, 1,
nodeGetRowid(pRtree, pNode, p->iCell));
rc = sqlite3_step(pCsr->pReadAux);
if( rc==SQLITE_ROW ){
pCsr->bAuxValid = 1;
}else{
sqlite3_reset(pCsr->pReadAux);
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
return rc;
}
}
sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pReadAux, i+2));
}
return SQLITE_OK;
}
static int geopolyUpdate(
sqlite3_vtab *pVtab,
int nData,
sqlite3_value **aData,
sqlite_int64 *pRowid
){
Rtree *pRtree = (Rtree *)pVtab;
int rc = SQLITE_OK;
RtreeCell cell;
i64 oldRowid;
int oldRowidValid;
i64 newRowid;
int newRowidValid;
int coordChange = 0;
if( pRtree->nNodeRef ){
return SQLITE_LOCKED_VTAB;
}
rtreeReference(pRtree);
assert(nData>=1);
oldRowidValid = sqlite3_value_type(aData[0])!=SQLITE_NULL;;
oldRowid = oldRowidValid ? sqlite3_value_int64(aData[0]) : 0;
newRowidValid = nData>1 && sqlite3_value_type(aData[1])!=SQLITE_NULL;
newRowid = newRowidValid ? sqlite3_value_int64(aData[1]) : 0;
cell.iRowid = newRowid;
if( nData>1
&& (!oldRowidValid
|| !sqlite3_value_nochange(aData[2])
|| oldRowid!=newRowid)
){
assert( aData[2]!=0 );
geopolyBBox(0, aData[2], cell.aCoord, &rc);
if( rc ){
if( rc==SQLITE_ERROR ){
pVtab->zErrMsg =
sqlite3_mprintf("_shape does not contain a valid polygon");
}
goto geopoly_update_end;
}
coordChange = 1;
if( newRowidValid && (!oldRowidValid || oldRowid!=newRowid) ){
int steprc;
sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid);
steprc = sqlite3_step(pRtree->pReadRowid);
rc = sqlite3_reset(pRtree->pReadRowid);
if( SQLITE_ROW==steprc ){
if( sqlite3_vtab_on_conflict(pRtree->db)==SQLITE_REPLACE ){
rc = rtreeDeleteRowid(pRtree, cell.iRowid);
}else{
rc = rtreeConstraintError(pRtree, 0);
}
}
}
}
if( rc==SQLITE_OK && (nData==1 || (coordChange && oldRowidValid)) ){
rc = rtreeDeleteRowid(pRtree, oldRowid);
}
if( rc==SQLITE_OK && nData>1 && coordChange ){
RtreeNode *pLeaf = 0;
if( !newRowidValid ){
rc = rtreeNewRowid(pRtree, &cell.iRowid);
}
*pRowid = cell.iRowid;
if( rc==SQLITE_OK ){
rc = ChooseLeaf(pRtree, &cell, 0, &pLeaf);
}
if( rc==SQLITE_OK ){
int rc2;
pRtree->iReinsertHeight = -1;
rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0);
rc2 = nodeRelease(pRtree, pLeaf);
if( rc==SQLITE_OK ){
rc = rc2;
}
}
}
if( rc==SQLITE_OK && nData>1 ){
sqlite3_stmt *pUp = pRtree->pWriteAux;
int jj;
int nChange = 0;
sqlite3_bind_int64(pUp, 1, cell.iRowid);
assert( pRtree->nAux>=1 );
if( sqlite3_value_nochange(aData[2]) ){
sqlite3_bind_null(pUp, 2);
}else{
GeoPoly *p = 0;
if( sqlite3_value_type(aData[2])==SQLITE_TEXT
&& (p = geopolyFuncParam(0, aData[2], &rc))!=0
&& rc==SQLITE_OK
){
sqlite3_bind_blob(pUp, 2, p->hdr, 4+8*p->nVertex, SQLITE_TRANSIENT);
}else{
sqlite3_bind_value(pUp, 2, aData[2]);
}
sqlite3_free(p);
nChange = 1;
}
for(jj=1; jj<nData-2; jj++){
nChange++;
sqlite3_bind_value(pUp, jj+2, aData[jj+2]);
}
if( nChange ){
sqlite3_step(pUp);
rc = sqlite3_reset(pUp);
}
}
geopoly_update_end:
rtreeRelease(pRtree);
return rc;
}
static int geopolyFindFunction(
sqlite3_vtab *pVtab,
int nArg,
const char *zName,
void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
void **ppArg
){
(void)pVtab;
(void)nArg;
if( sqlite3_stricmp(zName, "geopoly_overlap")==0 ){
*pxFunc = geopolyOverlapFunc;
*ppArg = 0;
return SQLITE_INDEX_CONSTRAINT_FUNCTION;
}
if( sqlite3_stricmp(zName, "geopoly_within")==0 ){
*pxFunc = geopolyWithinFunc;
*ppArg = 0;
return SQLITE_INDEX_CONSTRAINT_FUNCTION+1;
}
return 0;
}
static sqlite3_module geopolyModule = {
3,
geopolyCreate,
geopolyConnect,
geopolyBestIndex,
rtreeDisconnect,
rtreeDestroy,
rtreeOpen,
rtreeClose,
geopolyFilter,
rtreeNext,
rtreeEof,
geopolyColumn,
rtreeRowid,
geopolyUpdate,
rtreeBeginTransaction,
rtreeEndTransaction,
rtreeEndTransaction,
rtreeEndTransaction,
geopolyFindFunction,
rtreeRename,
rtreeSavepoint,
0,
0,
rtreeShadowName
};
static int sqlite3_geopoly_init(sqlite3 *db){
int rc = SQLITE_OK;
static const struct {
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
signed char nArg;
unsigned char bPure;
const char *zName;
} aFunc[] = {
{ geopolyAreaFunc, 1, 1, "geopoly_area" },
{ geopolyBlobFunc, 1, 1, "geopoly_blob" },
{ geopolyJsonFunc, 1, 1, "geopoly_json" },
{ geopolySvgFunc, -1, 1, "geopoly_svg" },
{ geopolyWithinFunc, 2, 1, "geopoly_within" },
{ geopolyContainsPointFunc, 3, 1, "geopoly_contains_point" },
{ geopolyOverlapFunc, 2, 1, "geopoly_overlap" },
{ geopolyDebugFunc, 1, 0, "geopoly_debug" },
{ geopolyBBoxFunc, 1, 1, "geopoly_bbox" },
{ geopolyXformFunc, 7, 1, "geopoly_xform" },
{ geopolyRegularFunc, 4, 1, "geopoly_regular" },
{ geopolyCcwFunc, 1, 1, "geopoly_ccw" },
};
static const struct {
void (*xStep)(sqlite3_context*,int,sqlite3_value**);
void (*xFinal)(sqlite3_context*);
const char *zName;
} aAgg[] = {
{ geopolyBBoxStep, geopolyBBoxFinal, "geopoly_group_bbox" },
};
unsigned int i;
for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
int enc;
if( aFunc[i].bPure ){
enc = SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS;
}else{
enc = SQLITE_UTF8|SQLITE_DIRECTONLY;
}
rc = sqlite3_create_function(db, aFunc[i].zName, aFunc[i].nArg,
enc, 0,
aFunc[i].xFunc, 0, 0);
}
for(i=0; i<sizeof(aAgg)/sizeof(aAgg[0]) && rc==SQLITE_OK; i++){
rc = sqlite3_create_function(db, aAgg[i].zName, 1,
SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS, 0,
0, aAgg[i].xStep, aAgg[i].xFinal);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_module_v2(db, "geopoly", &geopolyModule, 0, 0);
}
return rc;
}