1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840 |
- /*
- ** 2018-05-25
- **
- ** The author disclaims copyright to this source code. In place of
- ** a legal notice, here is a blessing:
- **
- ** May you do good and not evil.
- ** May you find forgiveness for yourself and forgive others.
- ** May you share freely, never taking more than you give.
- **
- ******************************************************************************
- **
- ** This file implements an alternative R-Tree virtual table that
- ** uses polygons to express the boundaries of 2-dimensional objects.
- **
- ** This file is #include-ed onto the end of "rtree.c" so that it has
- ** access to all of the R-Tree internals.
- */
- #include <stdlib.h>
- /* Enable -DGEOPOLY_ENABLE_DEBUG for debugging facilities */
- #ifdef GEOPOLY_ENABLE_DEBUG
- static int geo_debug = 0;
- # define GEODEBUG(X) if(geo_debug)printf X
- #else
- # define GEODEBUG(X)
- #endif
- /* Character class routines */
- #ifdef sqlite3Isdigit
- /* Use the SQLite core versions if this routine is part of the
- ** SQLite amalgamation */
- # define safe_isdigit(x) sqlite3Isdigit(x)
- # define safe_isalnum(x) sqlite3Isalnum(x)
- # define safe_isxdigit(x) sqlite3Isxdigit(x)
- #else
- /* Use the standard library for separate compilation */
- #include <ctype.h> /* amalgamator: keep */
- # 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 /* The following stuff repeats things found in json1 */
- /*
- ** Growing our own isspace() routine this way is twice as fast as
- ** the library isspace() function.
- */
- 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 /* JSON NULL - back to original code */
- /* Compiler and version */
- #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
- /* Datatype for coordinates
- */
- typedef float GeoCoord;
- /*
- ** Internal representation of a polygon.
- **
- ** The polygon consists of a sequence of vertexes. There is a line
- ** segment between each pair of vertexes, and one final segment from
- ** the last vertex back to the first. (This differs from the GeoJSON
- ** standard in which the final vertex is a repeat of the first.)
- **
- ** The polygon follows the right-hand rule. The area to the right of
- ** each segment is "outside" and the area to the left is "inside".
- **
- ** The on-disk representation consists of a 4-byte header followed by
- ** the values. The 4-byte header is:
- **
- ** encoding (1 byte) 0=big-endian, 1=little-endian
- ** nvertex (3 bytes) Number of vertexes as a big-endian integer
- **
- ** Enough space is allocated for 4 coordinates, to work around over-zealous
- ** warnings coming from some compiler (notably, clang). In reality, the size
- ** of each GeoPoly memory allocate is adjusted as necessary so that the
- ** GeoPoly.a[] array at the end is the appropriate size.
- */
- typedef struct GeoPoly GeoPoly;
- struct GeoPoly {
- int nVertex; /* Number of vertexes */
- unsigned char hdr[4]; /* Header for on-disk representation */
- GeoCoord a[8]; /* 2*nVertex values. X (longitude) first, then Y */
- };
- /* The size of a memory allocation needed for a GeoPoly object sufficient
- ** to hold N coordinate pairs.
- */
- #define GEOPOLY_SZ(N) (sizeof(GeoPoly) + sizeof(GeoCoord)*2*((N)-4))
- /* Macros to access coordinates of a GeoPoly.
- ** We have to use these macros, rather than just say p->a[i] in order
- ** to silence (incorrect) UBSAN warnings if the array index is too large.
- */
- #define GeoX(P,I) (((GeoCoord*)(P)->a)[(I)*2])
- #define GeoY(P,I) (((GeoCoord*)(P)->a)[(I)*2+1])
- /*
- ** State of a parse of a GeoJSON input.
- */
- typedef struct GeoParse GeoParse;
- struct GeoParse {
- const unsigned char *z; /* Unparsed input */
- int nVertex; /* Number of vertexes in a[] */
- int nAlloc; /* Space allocated to a[] */
- int nErr; /* Number of errors encountered */
- GeoCoord *a; /* Array of vertexes. From sqlite3_malloc64() */
- };
- /* Do a 4-byte byte swap */
- 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;
- }
- /* Skip whitespace. Return the next non-whitespace character. */
- static char geopolySkipSpace(GeoParse *p){
- while( fast_isspace(p->z[0]) ) p->z++;
- return p->z[0];
- }
- /* Parse out a number. Write the value into *pVal if pVal!=0.
- ** return non-zero on success and zero if the next token is not a number.
- */
- 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
- /* The sqlite3AtoF() routine is much much faster than atof(), if it
- ** is available */
- 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;
- }
- /*
- ** If the input is a well-formed JSON array of coordinates with at least
- ** four coordinates and where each coordinate is itself a two-value array,
- ** then convert the JSON into a GeoPoly object and return a pointer to
- ** that object.
- **
- ** If any error occurs, return NULL.
- */
- 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--; /* Remove the redundant vertex at the end */
- 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;
- }
- /*
- ** Given a function parameter, try to interpret it as a polygon, either
- ** in the binary format or JSON text. Compute a GeoPoly object and
- ** return a pointer to that object. Or if the input is not a well-formed
- ** polygon, put an error message in sqlite3_context and return NULL.
- */
- static GeoPoly *geopolyFuncParam(
- sqlite3_context *pCtx, /* Context for error messages */
- sqlite3_value *pVal, /* The value to decode */
- int *pRc /* Write error here */
- ){
- 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;
- }
- }
- /*
- ** Implementation of the geopoly_blob(X) function.
- **
- ** If the input is a well-formed Geopoly BLOB or JSON string
- ** then return the BLOB representation of the polygon. Otherwise
- ** return NULL.
- */
- 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);
- }
- }
- /*
- ** SQL function: geopoly_json(X)
- **
- ** Interpret X as a polygon and render it as a JSON array
- ** of coordinates. Or, if X is not a valid polygon, return NULL.
- */
- 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);
- }
- }
- /*
- ** SQL function: geopoly_svg(X, ....)
- **
- ** Interpret X as a polygon and render it as a SVG <polyline>.
- ** Additional arguments are added as attributes to the <polyline>.
- */
- 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);
- }
- }
- /*
- ** SQL Function: geopoly_xform(poly, A, B, C, D, E, F)
- **
- ** Transform and/or translate a polygon as follows:
- **
- ** x1 = A*x0 + B*y0 + E
- ** y1 = C*x0 + D*y0 + F
- **
- ** For a translation:
- **
- ** geopoly_xform(poly, 1, 0, 0, 1, x-offset, y-offset)
- **
- ** Rotate by R around the point (0,0):
- **
- ** geopoly_xform(poly, cos(R), sin(R), -sin(R), cos(R), 0, 0)
- */
- 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);
- }
- }
- /*
- ** Compute the area enclosed by the polygon.
- **
- ** This routine can also be used to detect polygons that rotate in
- ** the wrong direction. Polygons are suppose to be counter-clockwise (CCW).
- ** This routine returns a negative value for clockwise (CW) polygons.
- */
- 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)) /* (x0 - x1) */
- * (GeoY(p,ii) + GeoY(p,ii+1)) /* (y0 + y1) */
- * 0.5;
- }
- rArea += (GeoX(p,ii) - GeoX(p,0)) /* (xN - x0) */
- * (GeoY(p,ii) + GeoY(p,0)) /* (yN + y0) */
- * 0.5;
- return rArea;
- }
- /*
- ** Implementation of the geopoly_area(X) function.
- **
- ** If the input is a well-formed Geopoly BLOB then return the area
- ** enclosed by the polygon. If the polygon circulates clockwise instead
- ** of counterclockwise (as it should) then return the negative of the
- ** enclosed area. Otherwise return NULL.
- */
- 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);
- }
- }
- /*
- ** Implementation of the geopoly_ccw(X) function.
- **
- ** If the rotation of polygon X is clockwise (incorrect) instead of
- ** counter-clockwise (the correct winding order according to RFC7946)
- ** then reverse the order of the vertexes in polygon X.
- **
- ** In other words, this routine returns a CCW polygon regardless of the
- ** winding order of its input.
- **
- ** Use this routine to sanitize historical inputs that that sometimes
- ** contain polygons that wind in the wrong direction.
- */
- 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
- /* Fast approximation for sine(X) for X between -0.5*pi and 2*pi
- */
- 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;
- }
- }
- /*
- ** Function: geopoly_regular(X,Y,R,N)
- **
- ** Construct a simple, convex, regular polygon centered at X, Y
- ** with circumradius R and with N sides.
- */
- 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);
- }
- /*
- ** If pPoly is a polygon, compute its bounding box. Then:
- **
- ** (1) if aCoord!=0 store the bounding box in aCoord, returning NULL
- ** (2) otherwise, compute a GeoPoly for the bounding box and return the
- ** new GeoPoly
- **
- ** If pPoly is NULL but aCoord is not NULL, then compute a new GeoPoly from
- ** the bounding box in aCoord and return a pointer to that GeoPoly.
- */
- static GeoPoly *geopolyBBox(
- sqlite3_context *context, /* For recording the error */
- sqlite3_value *pPoly, /* The polygon */
- RtreeCoord *aCoord, /* Results here */
- int *pRc /* Error code here */
- ){
- 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;
- }
- /*
- ** Implementation of the geopoly_bbox(X) SQL function.
- */
- 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);
- }
- }
- /*
- ** State vector for the geopoly_group_bbox() aggregate function.
- */
- typedef struct GeoBBox GeoBBox;
- struct GeoBBox {
- int isInit;
- RtreeCoord a[4];
- };
- /*
- ** Implementation of the geopoly_group_bbox(X) aggregate SQL function.
- */
- 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);
- }
- }
- /*
- ** Determine if point (x0,y0) is beneath line segment (x1,y1)->(x2,y2).
- ** Returns:
- **
- ** +2 x0,y0 is on the line segement
- **
- ** +1 x0,y0 is beneath line segment
- **
- ** 0 x0,y0 is not on or beneath the line segment or the line segment
- ** is vertical and x0,y0 is not on the line segment
- **
- ** The left-most coordinate min(x1,x2) is not considered to be part of
- ** the line segment for the purposes of this analysis.
- */
- 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{
- /* Vertical line segment */
- 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;
- }
- /*
- ** SQL function: geopoly_contains_point(P,X,Y)
- **
- ** Return +2 if point X,Y is within polygon P.
- ** Return +1 if point X,Y is on the polygon boundary.
- ** Return 0 if point X,Y is outside the polygon
- */
- 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);
- }
- /* Forward declaration */
- static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2);
- /*
- ** SQL function: geopoly_within(P1,P2)
- **
- ** Return +2 if P1 and P2 are the same polygon
- ** Return +1 if P2 is contained within P1
- ** Return 0 if any part of P2 is on the outside of P1
- **
- */
- 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);
- }
- /* Objects used by the overlap algorihm. */
- typedef struct GeoEvent GeoEvent;
- typedef struct GeoSegment GeoSegment;
- typedef struct GeoOverlap GeoOverlap;
- struct GeoEvent {
- double x; /* X coordinate at which event occurs */
- int eType; /* 0 for ADD, 1 for REMOVE */
- GeoSegment *pSeg; /* The segment to be added or removed */
- GeoEvent *pNext; /* Next event in the sorted list */
- };
- struct GeoSegment {
- double C, B; /* y = C*x + B */
- double y; /* Current y value */
- float y0; /* Initial y value */
- unsigned char side; /* 1 for p1, 2 for p2 */
- unsigned int idx; /* Which segment within the side */
- GeoSegment *pNext; /* Next segment in a list sorted by y */
- };
- struct GeoOverlap {
- GeoEvent *aEvent; /* Array of all events */
- GeoSegment *aSegment; /* Array of all segments */
- int nEvent; /* Number of events */
- int nSegment; /* Number of segments */
- };
- /*
- ** Add a single segment and its associated events.
- */
- 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; /* Ignore vertical segments */
- 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;
- }
-
- /*
- ** Insert all segments and events for polygon pPoly.
- */
- static void geopolyAddSegments(
- GeoOverlap *p, /* Add segments to this Overlap object */
- GeoPoly *pPoly, /* Take all segments from this polygon */
- unsigned char side /* The side of pPoly */
- ){
- 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);
- }
- /*
- ** Merge two lists of sorted events by X coordinate
- */
- 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;
- }
- /*
- ** Sort an array of nEvent event objects into a list.
- */
- 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;
- }
- /*
- ** Merge two lists of sorted segments by Y, and then by C.
- */
- 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;
- }
- /*
- ** Sort a list of GeoSegments in order of increasing Y and in the event of
- ** a tie, increasing C (slope).
- */
- 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;
- }
- /*
- ** Determine the overlap between two polygons
- */
- 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 ){
- /* Add a segment */
- pSeg = pThisEvent->pSeg;
- pSeg->y = pSeg->y0;
- pSeg->pNext = pActive;
- pActive = pSeg;
- needSort = 1;
- }else{
- /* Remove a segment */
- 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;
- }
- /*
- ** SQL function: geopoly_overlap(P1,P2)
- **
- ** Determine whether or not P1 and P2 overlap. Return value:
- **
- ** 0 The two polygons are disjoint
- ** 1 They overlap
- ** 2 P1 is completely contained within P2
- ** 3 P2 is completely contained within P1
- ** 4 P1 and P2 are the same polygon
- ** NULL Either P1 or P2 or both are not valid polygons
- */
- 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);
- }
- /*
- ** Enable or disable debugging output
- */
- 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
- }
- /*
- ** This function is the implementation of both the xConnect and xCreate
- ** methods of the geopoly virtual table.
- **
- ** argv[0] -> module name
- ** argv[1] -> database name
- ** argv[2] -> table name
- ** argv[...] -> column names...
- */
- static int geopolyInit(
- sqlite3 *db, /* Database connection */
- void *pAux, /* One of the RTREE_COORD_* constants */
- int argc, const char *const*argv, /* Parameters to CREATE TABLE statement */
- sqlite3_vtab **ppVtab, /* OUT: New virtual table */
- char **pzErr, /* OUT: Error message, if any */
- int isCreate /* True for xCreate, false for xConnect */
- ){
- int rc = SQLITE_OK;
- Rtree *pRtree;
- sqlite3_int64 nDb; /* Length of string argv[1] */
- sqlite3_int64 nName; /* Length of string argv[2] */
- sqlite3_str *pSql;
- char *zSql;
- int ii;
- (void)pAux;
- sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
- sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
- /* Allocate the sqlite3_vtab structure */
- nDb = strlen(argv[1]);
- nName = strlen(argv[2]);
- pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName*2+8);
- if( !pRtree ){
- return SQLITE_NOMEM;
- }
- memset(pRtree, 0, sizeof(Rtree)+nDb+nName*2+8);
- pRtree->nBusy = 1;
- pRtree->base.pModule = &rtreeModule;
- pRtree->zDb = (char *)&pRtree[1];
- pRtree->zName = &pRtree->zDb[nDb+1];
- pRtree->zNodeName = &pRtree->zName[nName+1];
- pRtree->eCoordType = RTREE_COORD_REAL32;
- pRtree->nDim = 2;
- pRtree->nDim2 = 4;
- memcpy(pRtree->zDb, argv[1], nDb);
- memcpy(pRtree->zName, argv[2], nName);
- memcpy(pRtree->zNodeName, argv[2], nName);
- memcpy(&pRtree->zNodeName[nName], "_node", 6);
- /* Create/Connect to the underlying relational database schema. If
- ** that is successful, call sqlite3_declare_vtab() to configure
- ** the r-tree table schema.
- */
- pSql = sqlite3_str_new(db);
- sqlite3_str_appendf(pSql, "CREATE TABLE x(_shape");
- pRtree->nAux = 1; /* Add one for _shape */
- pRtree->nAuxNotNull = 1; /* The _shape column is always not-null */
- 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;
- /* Figure out the node size to use. */
- 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;
- }
- /*
- ** GEOPOLY virtual table module xCreate method.
- */
- 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);
- }
- /*
- ** GEOPOLY virtual table module xConnect method.
- */
- 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);
- }
- /*
- ** GEOPOLY virtual table module xFilter method.
- **
- ** Query plans:
- **
- ** 1 rowid lookup
- ** 2 search for objects overlapping the same bounding box
- ** that contains polygon argv[0]
- ** 3 search for objects overlapping the same bounding box
- ** that contains polygon argv[0]
- ** 4 full table scan
- */
- static int geopolyFilter(
- sqlite3_vtab_cursor *pVtabCursor, /* The cursor to initialize */
- int idxNum, /* Query plan */
- const char *idxStr, /* Not Used */
- int argc, sqlite3_value **argv /* Parameters to the query plan */
- ){
- Rtree *pRtree = (Rtree *)pVtabCursor->pVtab;
- RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
- RtreeNode *pRoot = 0;
- int rc = SQLITE_OK;
- int iCell = 0;
- (void)idxStr;
- rtreeReference(pRtree);
- /* Reset the cursor to the same state as rtreeOpen() leaves it in. */
- resetCursor(pCsr);
- pCsr->iStrategy = idxNum;
- if( idxNum==1 ){
- /* Special case - lookup by rowid. */
- RtreeNode *pLeaf; /* Leaf on which the required cell resides */
- RtreeSearchPoint *p; /* Search point for the leaf */
- 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 ); /* Always returns pCsr->sPoint */
- 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{
- /* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array
- ** with the configured constraints.
- */
- 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 ){
- /* Overlap query */
- 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{
- /* Within query */
- 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;
- }
- /*
- ** Rtree virtual table module xBestIndex method. There are three
- ** table scan strategies to choose from (in order from most to
- ** least desirable):
- **
- ** idxNum idxStr Strategy
- ** ------------------------------------------------
- ** 1 "rowid" Direct lookup by rowid.
- ** 2 "rtree" R-tree overlap query using geopoly_overlap()
- ** 3 "rtree" R-tree within query using geopoly_within()
- ** 4 "fullscan" full-table scan.
- ** ------------------------------------------------
- */
- 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 ){
- /* p->op==SQLITE_INDEX_CONSTRAINT_FUNCTION for geopoly_overlap()
- ** p->op==(SQLITE_INDEX_CONTRAINT_FUNCTION+1) for geopoly_within().
- ** See geopolyFindFunction() */
- 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;
- }
- /*
- ** GEOPOLY virtual table module xColumn method.
- */
- 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;
- }
- /*
- ** The xUpdate method for GEOPOLY module virtual tables.
- **
- ** For DELETE:
- **
- ** argv[0] = the rowid to be deleted
- **
- ** For INSERT:
- **
- ** argv[0] = SQL NULL
- ** argv[1] = rowid to insert, or an SQL NULL to select automatically
- ** argv[2] = _shape column
- ** argv[3] = first application-defined column....
- **
- ** For UPDATE:
- **
- ** argv[0] = rowid to modify. Never NULL
- ** argv[1] = rowid after the change. Never NULL
- ** argv[2] = new value for _shape
- ** argv[3] = new value for first application-defined column....
- */
- static int geopolyUpdate(
- sqlite3_vtab *pVtab,
- int nData,
- sqlite3_value **aData,
- sqlite_int64 *pRowid
- ){
- Rtree *pRtree = (Rtree *)pVtab;
- int rc = SQLITE_OK;
- RtreeCell cell; /* New cell to insert if nData>1 */
- i64 oldRowid; /* The old rowid */
- int oldRowidValid; /* True if oldRowid is valid */
- i64 newRowid; /* The new rowid */
- int newRowidValid; /* True if newRowid is valid */
- int coordChange = 0; /* Change in coordinates */
- if( pRtree->nNodeRef ){
- /* Unable to write to the btree while another cursor is reading from it,
- ** since the write might do a rebalance which would disrupt the read
- ** cursor. */
- 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 /* not a DELETE */
- && (!oldRowidValid /* INSERT */
- || !sqlite3_value_nochange(aData[2]) /* UPDATE _shape */
- || oldRowid!=newRowid) /* Rowid change */
- ){
- 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 a rowid value was supplied, check if it is already present in
- ** the table. If so, the constraint has failed. */
- 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 aData[0] is not an SQL NULL value, it is the rowid of a
- ** record to delete from the r-tree table. The following block does
- ** just that.
- */
- if( rc==SQLITE_OK && (nData==1 || (coordChange && oldRowidValid)) ){
- rc = rtreeDeleteRowid(pRtree, oldRowid);
- }
- /* If the aData[] array contains more than one element, elements
- ** (aData[2]..aData[argc-1]) contain a new record to insert into
- ** the r-tree structure.
- */
- if( rc==SQLITE_OK && nData>1 && coordChange ){
- /* Insert the new record into the r-tree */
- 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;
- rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0);
- rc2 = nodeRelease(pRtree, pLeaf);
- if( rc==SQLITE_OK ){
- rc = rc2;
- }
- }
- }
- /* Change the data */
- 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;
- }
- /*
- ** Report that geopoly_overlap() is an overloaded function suitable
- ** for use in xBestIndex.
- */
- 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, /* iVersion */
- geopolyCreate, /* xCreate - create a table */
- geopolyConnect, /* xConnect - connect to an existing table */
- geopolyBestIndex, /* xBestIndex - Determine search strategy */
- rtreeDisconnect, /* xDisconnect - Disconnect from a table */
- rtreeDestroy, /* xDestroy - Drop a table */
- rtreeOpen, /* xOpen - open a cursor */
- rtreeClose, /* xClose - close a cursor */
- geopolyFilter, /* xFilter - configure scan constraints */
- rtreeNext, /* xNext - advance a cursor */
- rtreeEof, /* xEof */
- geopolyColumn, /* xColumn - read data */
- rtreeRowid, /* xRowid - read data */
- geopolyUpdate, /* xUpdate - write data */
- rtreeBeginTransaction, /* xBegin - begin transaction */
- rtreeEndTransaction, /* xSync - sync transaction */
- rtreeEndTransaction, /* xCommit - commit transaction */
- rtreeEndTransaction, /* xRollback - rollback transaction */
- geopolyFindFunction, /* xFindFunction - function overloading */
- rtreeRename, /* xRename - rename the table */
- rtreeSavepoint, /* xSavepoint */
- 0, /* xRelease */
- 0, /* xRollbackTo */
- rtreeShadowName, /* xShadowName */
- rtreeIntegrity /* xIntegrity */
- };
- 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;
- }
|