SQLite

Check-in [2d4debccbc]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add the geopoly_group_bbox() aggregate function to the Geopoly module.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 2d4debccbc027405a33aeb10f9d65f6fe4bfb5eb1be5a4d8b82158caba04643f
User & Date: drh 2018-08-29 15:50:47.314
Context
2018-08-29
18:47
Free up the MEM_Frame bit in Mem.flags object. Store VdbeFrame objects as MEM_Blob with a special Mem.xDel pointer instead. (check-in: 62db5fd476 user: drh tags: trunk)
15:50
Add the geopoly_group_bbox() aggregate function to the Geopoly module. (check-in: 2d4debccbc user: drh tags: trunk)
2018-08-28
21:12
Disable the server1.test script on old PPC Macs due to problems in the pthreads implementation on those archaic machines. (check-in: 43efdd8c7e user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to ext/rtree/geopoly.c.
473
474
475
476
477
478
479




480

481
482
483
484
485
486
487
488
489
490
491











492
493
494
495
496
497
498
499
500
501
502
503
504
505
506

507
508
509
510
511
512
513
514


515
516
517
518
519
520
521
             * 0.5;
    sqlite3_result_double(context, rArea);
    sqlite3_free(p);
  }            
}

/*




** Compute a bound-box on a polygon.  Return a new GeoPoly object

** that describes the bounding box.  Or, if aCoord is not a NULL pointer
** fill it in with the bounding box instead.
*/
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 *p = geopolyFuncParam(context, pPoly, pRc);
  GeoPoly *pOut = 0;











  if( p ){
    int ii;
    float mnX, mxX, mnY, mxY;
    mnX = mxX = p->a[0];
    mnY = mxY = p->a[1];
    for(ii=1; ii<p->nVertex; ii++){
      double r = p->a[ii*2];
      if( r<mnX ) mnX = r;
      else if( r>mxX ) mxX = r;
      r = p->a[ii*2+1];
      if( r<mnY ) mnY = r;
      else if( r>mxY ) mxY = r;
    }
    if( pRc ) *pRc = SQLITE_OK;
    if( aCoord==0 ){

      pOut = sqlite3_realloc(p, sizeof(GeoPoly)+sizeof(GeoCoord)*6);
      if( pOut==0 ){
        sqlite3_free(p);
        if( context ) sqlite3_result_error_nomem(context);
        if( pRc ) *pRc = SQLITE_NOMEM;
        return 0;
      }
      pOut->nVertex = 4;


      pOut->hdr[1] = 0;
      pOut->hdr[2] = 0;
      pOut->hdr[3] = 4;
      pOut->a[0] = mnX;
      pOut->a[1] = mnY;
      pOut->a[2] = mxX;
      pOut->a[3] = mnY;







>
>
>
>
|
>
|
|







|
|
>
>
>
>
>
>
>
>
>
>
>


<












>








>
>







473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509

510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
             * 0.5;
    sqlite3_result_double(context, rArea);
    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 = p->a[0];
    mnY = mxY = p->a[1];
    for(ii=1; ii<p->nVertex; ii++){
      double r = p->a[ii*2];
      if( r<mnX ) mnX = r;
      else if( r>mxX ) mxX = r;
      r = p->a[ii*2+1];
      if( r<mnY ) mnY = r;
      else if( r>mxY ) mxY = r;
    }
    if( pRc ) *pRc = SQLITE_OK;
    if( aCoord==0 ){
      geopolyBboxFill:
      pOut = sqlite3_realloc(p, sizeof(GeoPoly)+sizeof(GeoCoord)*6);
      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*)&ii;
      pOut->hdr[1] = 0;
      pOut->hdr[2] = 0;
      pOut->hdr[3] = 4;
      pOut->a[0] = mnX;
      pOut->a[1] = mnY;
      pOut->a[2] = mxX;
      pOut->a[3] = mnY;
545
546
547
548
549
550
551




















































552
553
554
555
556
557
558
  GeoPoly *p = geopolyBBox(context, argv[0], 0, 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
**







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
  GeoPoly *p = geopolyBBox(context, argv[0], 0, 0);
  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)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
**
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
  pSql = sqlite3_str_new(db);
  sqlite3_str_appendf(pSql, "CREATE TABLE x(_shape");
  pRtree->nAux = 1;   /* Add one for _shape */
  for(ii=3; ii<argc; ii++){
    pRtree->nAux++;
    sqlite3_str_appendf(pSql, ",%s", argv[ii]);
  }
  sqlite3_str_appendf(pSql, ",_bbox HIDDEN);");
  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);







|







1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
  pSql = sqlite3_str_new(db);
  sqlite3_str_appendf(pSql, "CREATE TABLE x(_shape");
  pRtree->nAux = 1;   /* Add one for _shape */
  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);
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
      }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));
  }else{
    /* Must be the _bbox column */
  }
  return SQLITE_OK;
}


/*
** The xUpdate method for GEOPOLY module virtual tables.







<
<







1411
1412
1413
1414
1415
1416
1417


1418
1419
1420
1421
1422
1423
1424
      }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.
1555
1556
1557
1558
1559
1560
1561







1562
1563
1564
1565
1566
1567




1568
1569
1570
1571
1572
     { geopolyWithinFunc,        2,    "geopoly_within"           },
     { geopolyContainsPointFunc, 3,    "geopoly_contains_point"   },
     { geopolyOverlapFunc,       2,    "geopoly_overlap"          },
     { geopolyDebugFunc,         1,    "geopoly_debug"            },
     { geopolyBBoxFunc,          1,    "geopoly_bbox"             },
     { geopolyXformFunc,         7,    "geopoly_xform"            },
  };







  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);
  }




  if( rc==SQLITE_OK ){
    rc = sqlite3_create_module_v2(db, "geopoly", &geopolyModule, 0, 0);
  }
  return rc;
}







>
>
>
>
>
>
>






>
>
>
>





1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
     { geopolyWithinFunc,        2,    "geopoly_within"           },
     { geopolyContainsPointFunc, 3,    "geopoly_contains_point"   },
     { geopolyOverlapFunc,       2,    "geopoly_overlap"          },
     { geopolyDebugFunc,         1,    "geopoly_debug"            },
     { geopolyBBoxFunc,          1,    "geopoly_bbox"             },
     { geopolyXformFunc,         7,    "geopoly_xform"            },
  };
  static const struct {
    void (*xStep)(sqlite3_context*,int,sqlite3_value**);
    void (*xFinal)(sqlite3_context*);
    const char *zName;
  } aAgg[] = {
     { geopolyBBoxStep, geopolyBBoxFinal, "geopoly_group_bbox"    },
  };
  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);
  }
  for(i=0; i<sizeof(aAgg)/sizeof(aAgg[0]) && rc==SQLITE_OK; i++){
    rc = sqlite3_create_function(db, aAgg[i].zName, 1, SQLITE_UTF8, 0,
                                 0, aAgg[i].xStep, aAgg[i].xFinal);
  }
  if( rc==SQLITE_OK ){
    rc = sqlite3_create_module_v2(db, "geopoly", &geopolyModule, 0, 0);
  }
  return rc;
}
Changes to ext/rtree/visual01.txt.
395
396
397
398
399
400
401






















402
403
404
405
406
407
408
  FROM geo1, querypoly
 WHERE geopoly_overlap(_shape, poly);
SELECT geopoly_svg(poly, 
         printf('style="fill:%s;fill-opacity:0.5;"',clr)
       )
  FROM querypoly;
.print '</svg>'























.print '<h1>Bounding-Box Overlap Query</h1>'
.print '<svg width="1000" height="800" style="border:1px solid black">'
SELECT geopoly_svg(_shape,
         printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
       ),
       geopoly_svg(geopoly_bbox(_shape),







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
  FROM geo1, querypoly
 WHERE geopoly_overlap(_shape, poly);
SELECT geopoly_svg(poly, 
         printf('style="fill:%s;fill-opacity:0.5;"',clr)
       )
  FROM querypoly;
.print '</svg>'

.print '<h1>Overlap Query And Result Bounding Box</h1>'
.print '<svg width="1000" height="800" style="border:1px solid black">'
SELECT geopoly_svg(_shape,
         printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
       )
  FROM geo1, querypoly
 WHERE geopoly_overlap(_shape, poly);
SELECT geopoly_svg(geopoly_bbox(poly),
         'style="fill:none;stroke:black;stroke-width:3"'
       )
  FROM querypoly;
SELECT geopoly_svg(poly, 
         printf('style="fill:%s;fill-opacity:0.5;"',clr)
       )
  FROM querypoly;
SELECT geopoly_svg(geopoly_group_bbox(_shape),
         'style="fill:none;stroke:red;stroke-width:3"'
       )
  FROM geo1, querypoly
 WHERE geopoly_overlap(_shape, poly);
.print '</svg>'

.print '<h1>Bounding-Box Overlap Query</h1>'
.print '<svg width="1000" height="800" style="border:1px solid black">'
SELECT geopoly_svg(_shape,
         printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
       ),
       geopoly_svg(geopoly_bbox(_shape),