]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Forward port the geopoly extension functions into the r-tree extension,
authordrh <drh@noemail.net>
Fri, 25 May 2018 19:22:47 +0000 (19:22 +0000)
committerdrh <drh@noemail.net>
Fri, 25 May 2018 19:22:47 +0000 (19:22 +0000)
with the idea of creating a new spatial index based on simply polygons.

FossilOrigin-Name: 0593aac88a8c25ddafba4c29a181ee083dfc3dab44335feb6f12fdea6ce7fb27

Makefile.in
Makefile.msc
configure
configure.ac
ext/rtree/geopoly.c [new file with mode: 0644]
ext/rtree/rtree.c
main.mk
manifest
manifest.uuid
tool/mksqlite3c.tcl

index ac84fe832cbde6be814bc50a0a0386a27b701806..b8bcbd686431fd5fed69f9897b871102e55224b1 100644 (file)
@@ -349,7 +349,8 @@ SRC += \
   $(TOP)/ext/icu/icu.c
 SRC += \
   $(TOP)/ext/rtree/rtree.h \
-  $(TOP)/ext/rtree/rtree.c
+  $(TOP)/ext/rtree/rtree.c \
+  $(TOP)/ext/rtree/geopoly.c
 SRC += \
   $(TOP)/ext/session/sqlite3session.c \
   $(TOP)/ext/session/sqlite3session.h
@@ -549,7 +550,8 @@ EXTHDR += \
   $(TOP)/ext/fts3/fts3_hash.h \
   $(TOP)/ext/fts3/fts3_tokenizer.h
 EXTHDR += \
-  $(TOP)/ext/rtree/rtree.h
+  $(TOP)/ext/rtree/rtree.h \
+  $(TOP)/ext/rtree/geopoly.c
 EXTHDR += \
   $(TOP)/ext/icu/sqliteicu.h
 EXTHDR += \
index 8513996b6a0a887fcb55536368fe64cf3055cbc6..0c58cb181d612954e03a2e53c152b144ec6f9da5 100644 (file)
@@ -338,7 +338,7 @@ SQLITE_TCL_DEP =
 !IFNDEF OPT_FEATURE_FLAGS
 !IF $(MINIMAL_AMALGAMATION)==0
 OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
-OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_GEOPOLY=1
 !ENDIF
 OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
 !ENDIF
@@ -1400,6 +1400,7 @@ SRC09 = \
   $(TOP)\ext\fts3\fts3_tokenizer.h \
   $(TOP)\ext\icu\sqliteicu.h \
   $(TOP)\ext\rtree\rtree.h \
+  $(TOP)\ext\rtree\geopoly.c \
   $(TOP)\ext\rbu\sqlite3rbu.h \
   $(TOP)\ext\session\sqlite3session.h
 
@@ -1573,7 +1574,8 @@ EXTHDR = $(EXTHDR) \
   $(TOP)\ext\fts3\fts3_hash.h \
   $(TOP)\ext\fts3\fts3_tokenizer.h
 EXTHDR = $(EXTHDR) \
-  $(TOP)\ext\rtree\rtree.h
+  $(TOP)\ext\rtree\rtree.h \
+  $(TOP)\ext\rtree\geopoly.c
 EXTHDR = $(EXTHDR) \
   $(TOP)\ext\icu\sqliteicu.h
 EXTHDR = $(EXTHDR) \
index ea1160120f95045b77b929c3da23019ab581d4bf..c2a232b53c83f096b49829f25eb1dbf77f27026e 100755 (executable)
--- a/configure
+++ b/configure
@@ -911,6 +911,7 @@ enable_fts4
 enable_fts5
 enable_json1
 enable_update_limit
+enable_geopoly
 enable_rtree
 enable_session
 enable_gcov
@@ -1563,6 +1564,7 @@ Optional Features:
   --enable-fts5           Enable the FTS5 extension
   --enable-json1          Enable the JSON1 extension
   --enable-update-limit   Enable the UPDATE/DELETE LIMIT clause
+  --enable-geopoly        Enable the GEOPOLY extension
   --enable-rtree          Enable the RTREE extension
   --enable-session        Enable the SESSION extension
   --enable-gcov           Enable coverage testing using gcov
@@ -3932,13 +3934,13 @@ if ${lt_cv_nm_interface+:} false; then :
 else
   lt_cv_nm_interface="BSD nm"
   echo "int some_variable = 0;" > conftest.$ac_ext
-  (eval echo "\"\$as_me:3935: $ac_compile\"" >&5)
+  (eval echo "\"\$as_me:3937: $ac_compile\"" >&5)
   (eval "$ac_compile" 2>conftest.err)
   cat conftest.err >&5
-  (eval echo "\"\$as_me:3938: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+  (eval echo "\"\$as_me:3940: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
   (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
   cat conftest.err >&5
-  (eval echo "\"\$as_me:3941: output\"" >&5)
+  (eval echo "\"\$as_me:3943: output\"" >&5)
   cat conftest.out >&5
   if $GREP 'External.*some_variable' conftest.out > /dev/null; then
     lt_cv_nm_interface="MS dumpbin"
@@ -5144,7 +5146,7 @@ ia64-*-hpux*)
   ;;
 *-*-irix6*)
   # Find out which ABI we are using.
-  echo '#line 5147 "configure"' > conftest.$ac_ext
+  echo '#line 5149 "configure"' > conftest.$ac_ext
   if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
   (eval $ac_compile) 2>&5
   ac_status=$?
@@ -6669,11 +6671,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:6672: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:6674: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:6676: \$? = $ac_status" >&5
+   echo "$as_me:6678: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -7008,11 +7010,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:7011: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7013: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:7015: \$? = $ac_status" >&5
+   echo "$as_me:7017: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -7113,11 +7115,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:7116: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7118: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:7120: \$? = $ac_status" >&5
+   echo "$as_me:7122: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -7168,11 +7170,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:7171: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7173: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:7175: \$? = $ac_status" >&5
+   echo "$as_me:7177: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -9548,7 +9550,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 9551 "configure"
+#line 9553 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -9644,7 +9646,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 9647 "configure"
+#line 9649 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11630,6 +11632,20 @@ if test "${enable_udlimit}" = "yes" ; then
   OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT"
 fi
 
+#########
+# See whether we should enable GEOPOLY
+# Check whether --enable-geopoly was given.
+if test "${enable_geopoly+set}" = set; then :
+  enableval=$enable_geopoly; enable_geopoly=yes
+else
+  enable_geopoly=no
+fi
+
+if test "${enable_geopoly}" = "yes" ; then
+  OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_GEOPOLY"
+  enable_rtree=yes
+fi
+
 #########
 # See whether we should enable RTREE
 # Check whether --enable-rtree was given.
index 7089772d19785b9d6b55f6f95c9766d75dba1620..f9e347d969abc83e370d71cf356440c1b023f9c7 100644 (file)
@@ -660,6 +660,16 @@ if test "${enable_udlimit}" = "yes" ; then
   OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT"
 fi
 
+#########
+# See whether we should enable GEOPOLY
+AC_ARG_ENABLE(geopoly, AC_HELP_STRING([--enable-geopoly],
+      [Enable the GEOPOLY extension]),
+      [enable_geopoly=yes],[enable_geopoly=no])
+if test "${enable_geopoly}" = "yes" ; then
+  OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_GEOPOLY"
+  enable_rtree=yes
+fi
+
 #########
 # See whether we should enable RTREE
 AC_ARG_ENABLE(rtree, AC_HELP_STRING([--enable-rtree],
diff --git a/ext/rtree/geopoly.c b/ext/rtree/geopoly.c
new file mode 100644 (file)
index 0000000..3808871
--- /dev/null
@@ -0,0 +1,853 @@
+/*
+** 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
+
+#ifndef JSON_NULL   /* The following stuff repeats things found in json1 */
+/*
+** Versions of isspace(), isalnum() and isdigit() to which it is safe
+** to pass signed char values.
+*/
+#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
+
+/*
+** 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 safe_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
+*/
+typedef struct GeoPoly GeoPoly;
+struct GeoPoly {
+  int nVertex;          /* Number of vertexes */
+  unsigned char hdr[4]; /* Header for on-disk representation */
+  GeoCoord a[2];    /* 2*nVertex values. X (longitude) first, then Y */
+};
+
+/*
+** 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( p->z[0] && safe_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){
+  const unsigned char *z = p->z;
+  char c = geopolySkipSpace(p);
+  int j;
+  int seenDP = 0;
+  int seenE = 0;
+  assert( '-' < '0' );
+  if( c<='0' ){
+    j = c=='-';
+    if( z[j]=='0' && z[j+1]>='0' && z[j+1]<='9' ) return 0;
+  }
+  j = 1;
+  for(;; j++){
+    c = z[j];
+    if( c>='0' && c<='9' ) 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 ) *pVal = atof((const char*)p->z);
+  p->z += j;
+  return 1;
+}
+
+/*
+** If the input is a well-formed JSON array of coordinates, 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){
+  GeoParse s;
+  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 ){
+          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==']' ) break;
+        s.nErr++;
+        goto parse_json_err;
+      }
+      if( geopolySkipSpace(&s)==',' ){
+        s.z++;
+        continue;
+      }
+      break;
+    }
+    if( geopolySkipSpace(&s)==']' && s.nVertex>=4 ){
+      int nByte;
+      GeoPoly *pOut;
+      int x = (s.nVertex-1)*2;
+      if( s.a[x]==s.a[0] && s.a[x+1]==s.a[1] ) s.nVertex--;
+      nByte = sizeof(GeoPoly) * (s.nVertex-1)*2*sizeof(GeoCoord);
+      pOut = sqlite3_malloc64( nByte );
+      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);
+      return pOut;
+    }else{
+      s.nErr++;
+    }
+  }
+parse_json_err:
+  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, sqlite3_value *pVal){
+  GeoPoly *p = 0;
+  int nByte;
+  if( sqlite3_value_type(pVal)==SQLITE_BLOB
+   && (nByte = sqlite3_value_bytes(pVal))>=(4+6*sizeof(GeoCoord))
+  ){
+    const unsigned char *a = sqlite3_value_blob(pVal);
+    int nVertex;
+    nVertex = (a[1]<<16) + (a[2]<<8) + a[3];
+    if( (a[0]==0 && a[0]==1)
+     && (nVertex*2*sizeof(GeoCoord) + 4)==nByte
+    ){
+      p = sqlite3_malloc64( sizeof(*p) + (nVertex-1)*2*sizeof(GeoCoord) );
+      if( p ){
+        int x = 1;
+        p->nVertex = nVertex;
+        memcpy(p->hdr, a, nByte);
+        if( a[0] != *(unsigned char*)&x ){
+          int ii;
+          for(ii=0; ii<nVertex*2; ii++){
+            geopolySwab32((unsigned char*)&p->a[ii]);
+          }
+          p->hdr[0] ^= 1;
+        }
+      }
+    }
+  }else if( sqlite3_value_type(pVal)==SQLITE_TEXT ){
+    p = geopolyParseJson(sqlite3_value_text(pVal));
+  }
+  if( p==0 ){
+    sqlite3_result_error(pCtx, "not a valid polygon", -1);
+  }
+  return p;
+}
+
+/*
+** 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]);
+  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]);
+  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],", p->a[i*2], p->a[i*2+1]);
+    }
+    sqlite3_str_appendf(x, "[%!g,%!g]]", p->a[0], p->a[1]);
+    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 = geopolyFuncParam(context, argv[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, p->a[i*2], p->a[i*2+1]);
+      cSep = ' ';
+    }
+    sqlite3_str_appendf(x, " %g,%g'", p->a[0], p->a[1]);
+    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);
+  }
+}
+
+/*
+** 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]);
+  if( p ){
+    double rArea = 0.0;
+    int ii;
+    for(ii=0; ii<p->nVertex-1; ii++){
+      rArea += (p->a[ii*2] - p->a[ii*2+2])           /* (x0 - x1) */
+                * (p->a[ii*2+1] + p->a[ii*2+3])      /* (y0 + y1) */
+                * 0.5;
+    }
+    rArea += (p->a[ii*2] - p->a[0])                  /* (xN - x0) */
+             * (p->a[ii*2+1] + p->a[1])              /* (yN + y0) */
+             * 0.5;
+    sqlite3_result_double(context, rArea);
+    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_within(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 geopolyWithinFunc(
+  sqlite3_context *context,
+  int argc,
+  sqlite3_value **argv
+){
+  GeoPoly *p = geopolyFuncParam(context, argv[0]);
+  double x0 = sqlite3_value_double(argv[1]);
+  double y0 = sqlite3_value_double(argv[2]);
+  if( p ){
+    int v = 0;
+    int cnt = 0;
+    int ii;
+    for(ii=0; ii<p->nVertex-1; ii++){
+      v = pointBeneathLine(x0,y0,p->a[ii*2],p->a[ii*2+1],
+                                 p->a[ii*2+2],p->a[ii*2+3]);
+      if( v==2 ) break;
+      cnt += v;
+    }
+    if( v!=2 ){
+      v = pointBeneathLine(x0,y0,p->a[ii*2],p->a[ii*2+1],
+                                 p->a[0],p->a[1]);
+    }
+    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(p);
+  }            
+}
+
+/* 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<pPoly->nVertex-1; i++){
+    x = pPoly->a + (i*2);
+    geopolyAddOneSegment(p, x[0], x[1], x[2], x[3], side, i);
+  }
+  x = pPoly->a + (i*2);
+  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){
+  int nVertex = p1->nVertex + p2->nVertex + 2;
+  GeoOverlap *p;
+  int 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_malloc( 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->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 = pActive->pNext;
+      }else{
+        for(pSeg=pActive; pSeg; pSeg=pSeg->pNext){
+          if( pSeg->pNext==pThisEvent->pSeg ){
+            pSeg->pNext = pSeg->pNext->pNext;
+            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]);
+  GeoPoly *p2 = geopolyFuncParam(context, argv[1]);
+  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
+){
+#ifdef GEOPOLY_ENABLE_DEBUG
+  geo_debug = sqlite3_value_int(argv[0]);
+#endif
+}
+
+static int sqlite3_geopoly_init(sqlite3 *db){
+  int rc = SQLITE_OK;
+  static const struct {
+    void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
+    int nArg;
+    const char *zName;
+  } aFunc[] = {
+     { geopolyAreaFunc,          1,    "geopoly_area"     },
+     { geopolyBlobFunc,          1,    "geopoly_blob"     },
+     { geopolyJsonFunc,          1,    "geopoly_json"     },
+     { geopolySvgFunc,          -1,    "geopoly_svg"      },
+     { geopolyWithinFunc,        3,    "geopoly_within"   },
+     { geopolyOverlapFunc,       2,    "geopoly_overlap"  },
+     { geopolyDebugFunc,         1,    "geopoly_debug"    },
+  };
+  int i;
+  for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
+    rc = sqlite3_create_function(db, aFunc[i].zName, aFunc[i].nArg,
+                                 SQLITE_UTF8, 0,
+                                 aFunc[i].xFunc, 0, 0);
+  }
+  return rc;
+}
index e1b16ec0f5d04919183e2873c6db86193433f567..d817823f9671a94c548c07c6342d45e4d081b73d 100644 (file)
@@ -4222,6 +4222,10 @@ static void rtreecheck(
   }
 }
 
+/* Conditionally include the geopoly code */
+#ifdef SQLITE_ENABLE_GEOPOLY
+# include "geopoly.c"
+#endif
 
 /*
 ** Register the r-tree module with database handle db. This creates the
@@ -4251,6 +4255,11 @@ int sqlite3RtreeInit(sqlite3 *db){
     void *c = (void *)RTREE_COORD_INT32;
     rc = sqlite3_create_module_v2(db, "rtree_i32", &rtreeModule, c, 0);
   }
+#ifdef SQLITE_ENABLE_GEOPOLY
+  if( rc==SQLITE_OK ){
+    rc = sqlite3_geopoly_init(db);
+  }
+#endif
 
   return rc;
 }
diff --git a/main.mk b/main.mk
index 8dc423c5b51c7bd636e74a06dd1b1ca3cc3b2f3b..5109496e0e494710af88fffbccedbf503426b433 100644 (file)
--- a/main.mk
+++ b/main.mk
@@ -228,7 +228,8 @@ SRC += \
 SRC += \
   $(TOP)/ext/rtree/sqlite3rtree.h \
   $(TOP)/ext/rtree/rtree.h \
-  $(TOP)/ext/rtree/rtree.c
+  $(TOP)/ext/rtree/rtree.c \
+  $(TOP)/ext/rtree/geopoly.c
 SRC += \
   $(TOP)/ext/session/sqlite3session.c \
   $(TOP)/ext/session/sqlite3session.h
@@ -473,7 +474,8 @@ EXTHDR += \
   $(TOP)/ext/fts3/fts3_hash.h \
   $(TOP)/ext/fts3/fts3_tokenizer.h
 EXTHDR += \
-  $(TOP)/ext/rtree/rtree.h
+  $(TOP)/ext/rtree/rtree.h \
+  $(TOP)/ext/rtree/geopoly.c
 EXTHDR += \
   $(TOP)/ext/icu/sqliteicu.h
 EXTHDR += \
index 721b442bbb03e01bfea1a6acec20f534735fbb1f..b18592bff6a5c0c2d7790ed8fa947374c21b2db0 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,10 +1,10 @@
-C When\sdoing\sa\sone-pass\sUPDATE\sor\sDELETE\son\svirtual\stables,\sclose\sthe\scursor\nprior\sto\srunning\sVUpdate.\s\sThis\sallows\sone-pass\sto\swork\son\svirtual\stables\nthat\sdo\snot\sallow\sconcurrent\sreads\sand\swrites.\s\sEnhance\srtree\sto\stake\nadvantage\sof\sthis\snew\scapability.
-D 2018-05-24T23:51:57.743
+C Forward\sport\sthe\sgeopoly\sextension\sfunctions\sinto\sthe\sr-tree\sextension,\nwith\sthe\sidea\sof\screating\sa\snew\sspatial\sindex\sbased\son\ssimply\spolygons.
+D 2018-05-25T19:22:47.258
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
-F Makefile.in bfc40f350586923e0419d2ea4b559c37ec10ee4b6e210e08c14401f8e340f0da
+F Makefile.in 51407f0e371dcb9e65d368bd4f1a08fc17ef8361ff11aac9356f0f63693b38dd
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
-F Makefile.msc 681fb88cccf1fd58c0b9648f6a09b75332206ef72ca76012ad11699c320cec5f
+F Makefile.msc 550559948ff2bfccff07186842fcae6c25ec93468f42bace545761543f07c25d
 F README.md 7764d56778d567913ef11c82da9ab94aefa0826f7c243351e4e2d7adaef6f373
 F VERSION b7c9d1d11cb70ef8e90cfcf3c944aa58a9f801cc2ad487eebb0a110c16dfc2df
 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
@@ -32,8 +32,8 @@ F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
 F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
 F config.h.in 6376abec766e9a0785178b1823b5a587e9f1ccbc
 F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
-F configure 80e2dcad8ab88aacc58b55eb0e395f79184b45fcfaa3f36fc20d2e71cfa0a7e4 x
-F configure.ac d4529ebb26ae046269334f1dac65f2b1d6927c2efe22b2ec24dce24dfe4f83dd
+F configure dbc9bec9360ff31571dfd3df4dd5b41f01ba42a5bca44d2d7f49ef16cc34ae58 x
+F configure.ac bfd8b71640bbff1088b7ff7e48012872c697141d46cff24382fd90f4c5919c88
 F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
 F doc/lemon.html ac63db056bce24b7368e29319cd1a7eb5f1798cc85922d96a80b6c3a4ff9f51b
 F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710
@@ -355,7 +355,8 @@ F ext/repair/test/checkfreelist01.test 3e8aa6aeb4007680c94a8d07b41c339aa635cc782
 F ext/repair/test/checkindex01.test 6945d0ffc0c1dc993b2ce88036b26e0f5d6fcc65da70fc9df27c2647bb358b0f
 F ext/repair/test/test.tcl 686d76d888dffd021f64260abf29a55c57b2cedfa7fc69150b42b1d6119aac3c
 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
-F ext/rtree/rtree.c cb6d4bd43c118354fe5b5213843da058259467ecdbac0c6f71ead0fd89acf4ec
+F ext/rtree/geopoly.c bde931e25e378780f50650bab8810a868e85dbea70f29188193d717eb121c476 w ext/rtree/polygon.c
+F ext/rtree/rtree.c 2fd3c149c6fc4d3fdf602dc610b34ad9abdf75cca26d0c362f903aa02ea2ef47
 F ext/rtree/rtree.h 4a690463901cb5e6127cf05eb8e642f127012fd5003830dbc974eca5802d9412
 F ext/rtree/rtree1.test 309afc04d4287542b2cd74f933296832cc681c7b014d9405cb329b62053a5349
 F ext/rtree/rtree2.test 5f25b01acd03470067a2d52783b2eb0a50bf836803d4342d20ca39e541220fe2
@@ -416,7 +417,7 @@ F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
-F main.mk 3f50dfe5cb4257c1aca96e417636ed51bc2561e71d31a21e9ccdf66feb912f43
+F main.mk d32db280e80805104f8a9aeadcea268cbb38e89bbc52fb5313d7af820de3b138
 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -1667,7 +1668,7 @@ F tool/mkshellc.tcl 1f45770aea226ac093a9c72f718efbb88a2a2833409ec2e1c4cecae42026
 F tool/mksourceid.c d458f9004c837bee87a6382228ac20d3eae3c49ea3b0a5aace936f8b60748d3b
 F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
 F tool/mksqlite3c-noext.tcl fef88397668ae83166735c41af99d79f56afaabb
-F tool/mksqlite3c.tcl d532ccbe81234f766bab6e5c0451c99529debcea138caccc1862a9645b2d54f2
+F tool/mksqlite3c.tcl ff9895dc21c300076c7311500e7663db0d837d1dad3f6c91a97ac8a33627a4af
 F tool/mksqlite3h.tcl 080873e3856eceb9d289a08a00c4b30f875ea3feadcbece796bd509b1532792c
 F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b
 F tool/mkvsix.tcl b9e0777a213c23156b6542842c238479e496ebf5
@@ -1729,7 +1730,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 3ba08e53d54165f5541756ad13a4c2f0b18516cb612a256e056ed1ff76c1fa83
-R 52623d992549c096857f92f7f6ff2464
+P b816023ce07d01024d5769e16db924374a49bf909edd12dc1344a0a1ef693db5
+R c65edfd057806e76a349cce4c936be8c
+T *branch * rtree-geopoly
+T *sym-rtree-geopoly *
+T -sym-trunk *
 U drh
-Z 92fa856c89ee79b753ba76005690f976
+Z 27c7d298825e4848f2fc61f45fe7bda5
index 5d8567b91b1bf43e14be74cd60a7a1be93e43be1..752a1ea953fe422aabe286760f12e936f7e7141e 100644 (file)
@@ -1 +1 @@
-b816023ce07d01024d5769e16db924374a49bf909edd12dc1344a0a1ef693db5
\ No newline at end of file
+0593aac88a8c25ddafba4c29a181ee083dfc3dab44335feb6f12fdea6ce7fb27
\ No newline at end of file
index 6c1dab6accca31aca34dd1ea03f56a76f27ebcd1..b8371eed32e8c23fca13851284961f05900e63f6 100644 (file)
@@ -99,6 +99,7 @@ foreach hdr {
    fts3Int.h
    fts3_hash.h
    fts3_tokenizer.h
+   geopoly.c
    hash.h
    hwtime.h
    keywordhash.h
@@ -391,6 +392,7 @@ foreach file {
    fts3_unicode.c
    fts3_unicode2.c
 
+   json1.c
    rtree.c
    icu.c
    fts3_icu.c
@@ -398,7 +400,6 @@ foreach file {
    dbstat.c
    dbpage.c
    sqlite3session.c
-   json1.c
    fts5.c
    stmt.c
 } {