diff --git a/.gitignore b/.gitignore index d71adf6e..3fba0510 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ Makefile */*/Makefile pgsql/pointcloud.control pgsql/pointcloud--* +pgsql_postgis/pointcloud_postgis--*.sql +pgsql_postgis/pointcloud_postgis.control diff --git a/README.md b/README.md index 795bea5c..1df4448e 100644 --- a/README.md +++ b/README.md @@ -276,12 +276,12 @@ Now that you have created two tables, you'll see entries for them in the `pointc > > 1 -**PC_Envelope(p pcpatch)** returns **bytea** +**PC_Envelope_AsBinary(p pcpatch)** returns **bytea** > Return the OGC "well-known binary" format for *bounds* of the patch. > Useful for performing intersection tests with geometries. > -> SELECT PC_Envelope(pa) FROM patches LIMIT 1; +> SELECT PC_Envelope_AsBinary(pa) FROM patches LIMIT 1; > > \x0103000000010000000500000090c2f5285cbf5fc0e17a > 14ae4781464090c2f5285cbf5fc0ec51b81e858b46400ad7 @@ -519,6 +519,11 @@ The `pointcloud_postgis` extension adds functions that allow you to use PostgreS > > POINT Z (-127 45 124) +**PC_Envelope(pcpatch)** returns **geometry**
+**pcpatch::geometry** returns **geometry** + +> Get the PcPatch bounds as a PostGIS geometry + ## Compressions ## One of the issues with LIDAR data is that there is a lot of it. To deal with data volumes, PostgreSQL Pointcloud allows schemas to declare their preferred compression method in the `` block of the schema document. In the example schema, we declared our compression as follows: diff --git a/lib/cunit/cu_pc_patch.c b/lib/cunit/cu_pc_patch.c index 5da8161e..b065a0d8 100644 --- a/lib/cunit/cu_pc_patch.c +++ b/lib/cunit/cu_pc_patch.c @@ -454,8 +454,11 @@ test_patch_wkb() PCPOINTLIST *pl1; PCPATCH_UNCOMPRESSED *pu1, *pu2; PCPATCH *pa1, *pa2, *pa3, *pa4; - size_t z1, z2; - uint8_t *wkb1, *wkb2; + size_t z1, z2, z3; + uint8_t *wkb1, *wkb2, *wkb3, *hexwkb; + + static char *hexresult_ndr = "01030000000100000005000000000000000000000000000000000000000000000000000000CDCCCCCCCC8C4B40EC51B81E852B4440CDCCCCCCCC8C4B40EC51B81E852B4440000000000000000000000000000000000000000000000000"; + static char *hexresult_xdr = "00000000030000000100000005000000000000000000000000000000000000000000000000404B8CCCCCCCCCCD40442B851EB851EC404B8CCCCCCCCCCD40442B851EB851EC000000000000000000000000000000000000000000000000"; pl1 = pc_pointlist_make(npts); @@ -474,6 +477,7 @@ test_patch_wkb() // str = hexbytes_from_bytes(wkb1, z1); // printf("str\n%s\n",str); pa2 = pc_patch_from_wkb(simpleschema, wkb1, z1); + pcfree(wkb1); // printf("pa2\n%s\n",pc_patch_to_string(pa2)); @@ -497,6 +501,18 @@ test_patch_wkb() CU_ASSERT_EQUAL(pu1->npoints, pu2->npoints); CU_ASSERT(memcmp(pu1->data, pu2->data, pu1->datasize) == 0); + wkb3 = pc_bounds_to_geometry_wkb(&pa1->bounds,simpleschema->srid,&z3); + hexwkb = hexbytes_from_bytes(wkb3,z3); + if ( machine_endian() == PC_NDR ) + { + CU_ASSERT_STRING_EQUAL(hexwkb, hexresult_ndr); + } + else + { + CU_ASSERT_STRING_EQUAL(hexwkb, hexresult_xdr); + } + pcfree(hexwkb); + pcfree(wkb3); pc_pointlist_free(pl1); pc_patch_free(pa1); @@ -505,7 +521,6 @@ test_patch_wkb() pc_patch_free(pa4); pc_patch_free((PCPATCH*)pu1); pc_patch_free((PCPATCH*)pu2); - pcfree(wkb1); } diff --git a/lib/cunit/cu_pc_schema.c b/lib/cunit/cu_pc_schema.c index c09b7e17..5bbf077c 100644 --- a/lib/cunit/cu_pc_schema.c +++ b/lib/cunit/cu_pc_schema.c @@ -205,9 +205,9 @@ test_schema_clone(void) pc_schema_free(clone); /* See https://github.com/pgpointcloud/pointcloud/issues/66 */ - xmlstr = "1"; + xmlstr = "1"; i = pc_schema_from_xml(xmlstr, &myschema); - CU_ASSERT_EQUAL(i, PC_SUCCESS); + CU_ASSERT_EQUAL(i, PC_SUCCESS); clone = pc_schema_clone(myschema); CU_ASSERT_EQUAL(clone->ndims, myschema->ndims); CU_ASSERT_EQUAL(clone->dims[0]->name, NULL); diff --git a/lib/pc_api.h b/lib/pc_api.h index a8647912..7291a223 100644 --- a/lib/pc_api.h +++ b/lib/pc_api.h @@ -150,8 +150,19 @@ typedef struct double xmax; double ymin; double ymax; + double zmin; + double zmax; + double mmin; + double mmax; } PCBOUNDS; +typedef struct +{ + double xmin, ymin, zmin; + double xmax, ymax, zmax; + int32_t srid; +} PCBOX3D; + /* Used for generic patch statistics */ typedef struct { @@ -437,6 +448,12 @@ int pc_patch_compute_extent(PCPATCH *patch); /** True/false if bounds intersect */ int pc_bounds_intersects(const PCBOUNDS *b1, const PCBOUNDS *b2); +/** Return the bounds as an OGC WKB geometry */ +uint8_t *pc_bounds_to_geometry_wkb(const PCBOUNDS *bounds, uint32_t srid, size_t *wkbsize); + +/** Return the bounds as a BOX3D */ +PCBOX3D *pc_bounds_to_box3d(const PCBOUNDS *bounds, uint32_t srid); + /** Subset batch based on less-than condition on dimension */ PCPATCH* pc_patch_filter_lt_by_name(const PCPATCH *pa, const char *name, double val); diff --git a/lib/pc_api_internal.h b/lib/pc_api_internal.h index be7d2a32..0e7e4d91 100644 --- a/lib/pc_api_internal.h +++ b/lib/pc_api_internal.h @@ -269,11 +269,13 @@ void pc_bytes_to_ptr(uint8_t *buf, PCBYTES pcb, int n); */ /** Initialize with very large mins and very small maxes */ -void pc_bounds_init(PCBOUNDS *b); +void pc_bounds_init(PCBOUNDS *b, const PCSCHEMA *schema); /** Copy a bounds */ PCSTATS* pc_stats_clone(const PCSTATS *stats); /** Expand extents of b1 to encompass b2 */ void pc_bounds_merge(PCBOUNDS *b1, const PCBOUNDS *b2); +/** Expand extents of b to encompass p */ +void pc_bounds_expand(PCBOUNDS *b, const PCPOINT *p); /**************************************************************************** * BITMAPS diff --git a/lib/pc_filter.c b/lib/pc_filter.c index 7255b1e5..fc1fe965 100644 --- a/lib/pc_filter.c +++ b/lib/pc_filter.c @@ -165,7 +165,7 @@ pc_patch_dimensional_filter(const PCPATCH_DIMENSIONAL *pdl, const PCBITMAP *map) stats.max = pc_value_scale_offset(stats.max, dim); stats.sum = pc_value_scale_offset(stats.sum, dim); - /* Save the X/Y stats for use in bounds later */ + /* Save the XYZM stats for use in bounds later */ if ( i == pdl->schema->x_position ) { fpdl->bounds.xmin = stats.min; @@ -176,6 +176,16 @@ pc_patch_dimensional_filter(const PCPATCH_DIMENSIONAL *pdl, const PCBITMAP *map) fpdl->bounds.ymin = stats.min; fpdl->bounds.ymax = stats.max; } + else if ( i == pdl->schema->z_position ) + { + fpdl->bounds.zmin = stats.min; + fpdl->bounds.zmax = stats.max; + } + else if ( i == pdl->schema->m_position ) + { + fpdl->bounds.mmin = stats.min; + fpdl->bounds.mmax = stats.max; + } pc_point_set_double_by_index(&(fpdl->stats->min), i, stats.min); pc_point_set_double_by_index(&(fpdl->stats->max), i, stats.max); diff --git a/lib/pc_patch_dimensional.c b/lib/pc_patch_dimensional.c index 840f44b1..8416e6f5 100644 --- a/lib/pc_patch_dimensional.c +++ b/lib/pc_patch_dimensional.c @@ -21,7 +21,7 @@ typedef struct int8_t readonly; const PCSCHEMA *schema; uint32_t npoints; - double xmin, xmax, ymin, ymax; + double xmin, xmax, ymin, ymax, zmin, zmax, mmin, mmax; PCSTATS *stats; PCBYTES *bytes; } PCPATCH_DIMENSIONAL; @@ -179,30 +179,54 @@ pc_patch_dimensional_free(PCPATCH_DIMENSIONAL *pdl) int pc_patch_dimensional_compute_extent(PCPATCH_DIMENSIONAL *pdl) { - double xmin, xmax, ymin, ymax, xavg, yavg; + double min, max, avg; int rv; PCBYTES *pcb; assert(pdl); assert(pdl->schema); + pc_bounds_init(&(pdl->bounds), pdl->schema); + /* Get x extremes */ - pcb = &(pdl->bytes[pdl->schema->x_position]); - rv = pc_bytes_minmax(pcb, &xmin, &xmax, &xavg); - if ( PC_FAILURE == rv ) return PC_FAILURE; - xmin = pc_value_scale_offset(xmin, pdl->schema->dims[pdl->schema->x_position]); - xmax = pc_value_scale_offset(xmax, pdl->schema->dims[pdl->schema->x_position]); - pdl->bounds.xmin = xmin; - pdl->bounds.xmax = xmax; + if(pdl->schema->x_position!=-1) + { + pcb = &(pdl->bytes[pdl->schema->x_position]); + rv = pc_bytes_minmax(pcb, &min, &max, &avg); + if ( PC_FAILURE == rv ) return PC_FAILURE; + pdl->bounds.xmin = pc_value_scale_offset(min, pdl->schema->dims[pdl->schema->x_position]); + pdl->bounds.xmax = pc_value_scale_offset(max, pdl->schema->dims[pdl->schema->x_position]); + } /* Get y extremes */ - pcb = &(pdl->bytes[pdl->schema->y_position]); - rv = pc_bytes_minmax(pcb, &ymin, &ymax, &yavg); - if ( PC_FAILURE == rv ) return PC_FAILURE; - ymin = pc_value_scale_offset(ymin, pdl->schema->dims[pdl->schema->y_position]); - ymax = pc_value_scale_offset(ymax, pdl->schema->dims[pdl->schema->y_position]); - pdl->bounds.ymin = ymin; - pdl->bounds.ymax = ymax; + if(pdl->schema->y_position!=-1) + { + pcb = &(pdl->bytes[pdl->schema->y_position]); + rv = pc_bytes_minmax(pcb, &min, &max, &avg); + if ( PC_FAILURE == rv ) return PC_FAILURE; + pdl->bounds.ymin = pc_value_scale_offset(min, pdl->schema->dims[pdl->schema->y_position]); + pdl->bounds.ymax = pc_value_scale_offset(max, pdl->schema->dims[pdl->schema->y_position]); + } + + /* Get z extremes */ + if(pdl->schema->z_position!=-1) + { + pcb = &(pdl->bytes[pdl->schema->z_position]); + rv = pc_bytes_minmax(pcb, &min, &max, &avg); + if ( PC_FAILURE == rv ) return PC_FAILURE; + pdl->bounds.zmin = pc_value_scale_offset(min, pdl->schema->dims[pdl->schema->z_position]); + pdl->bounds.zmax = pc_value_scale_offset(max, pdl->schema->dims[pdl->schema->z_position]); + } + + /* Get m extremes */ + if(pdl->schema->m_position!=-1) + { + pcb = &(pdl->bytes[pdl->schema->m_position]); + rv = pc_bytes_minmax(pcb, &min, &max, &avg); + if ( PC_FAILURE == rv ) return PC_FAILURE; + pdl->bounds.mmin = pc_value_scale_offset(min, pdl->schema->dims[pdl->schema->m_position]); + pdl->bounds.mmax = pc_value_scale_offset(max, pdl->schema->dims[pdl->schema->m_position]); + } return PC_SUCCESS; } diff --git a/lib/pc_patch_ght.c b/lib/pc_patch_ght.c index 679af86b..4c514350 100644 --- a/lib/pc_patch_ght.c +++ b/lib/pc_patch_ght.c @@ -402,6 +402,12 @@ pc_patch_ght_compute_extent(PCPATCH_GHT *patch) patch->bounds.ymin = area.y.min; patch->bounds.ymax = area.y.max; + // provide a conservative non-discriminative bounding box + patch->bounds.zmin = -DBLMAX; + patch->bounds.zmax = DBLMAX; + patch->bounds.mmin = -DBLMAX; + patch->bounds.mmax = DBLMAX; + // ght_tree_free(tree); return PC_SUCCESS; diff --git a/lib/pc_patch_uncompressed.c b/lib/pc_patch_uncompressed.c index 810f6e36..3fe147c8 100644 --- a/lib/pc_patch_uncompressed.c +++ b/lib/pc_patch_uncompressed.c @@ -180,7 +180,7 @@ pc_patch_uncompressed_make(const PCSCHEMA *s, uint32_t maxpoints) { pch->data = pcalloc(datasize); } - pc_bounds_init(&(pch->bounds)); + pc_bounds_init(&(pch->bounds), s); return pch; } @@ -190,24 +190,17 @@ pc_patch_uncompressed_compute_extent(PCPATCH_UNCOMPRESSED *patch) { int i; PCPOINT *pt = pc_point_from_data(patch->schema, patch->data); - PCBOUNDS b; - double x, y; + double v; /* Calculate bounds */ - pc_bounds_init(&b); + pc_bounds_init(&(patch->bounds), patch->schema); for ( i = 0; i < patch->npoints; i++ ) { /* Just push the data buffer forward by one point at a time */ pt->data = patch->data + i * patch->schema->size; - x = pc_point_get_x(pt); - y = pc_point_get_y(pt); - if ( b.xmin > x ) b.xmin = x; - if ( b.ymin > y ) b.ymin = y; - if ( b.xmax < x ) b.xmax = x; - if ( b.ymax < y ) b.ymax = y; + pc_bounds_expand(&(patch->bounds),pt); } - patch->bounds = b; pcfree(pt); return PC_SUCCESS; } @@ -282,7 +275,7 @@ pc_patch_uncompressed_from_pointlist(const PCPOINTLIST *pl) ptr = pch->data; /* Initialize bounds */ - pc_bounds_init(&(pch->bounds)); + pc_bounds_init(&(pch->bounds), s); /* Set up basic info */ pch->readonly = PC_FALSE; @@ -418,12 +411,7 @@ pc_patch_uncompressed_add_point(PCPATCH_UNCOMPRESSED *c, const PCPOINT *p) c->npoints += 1; /* Update bounding box */ - x = pc_point_get_x(p); - y = pc_point_get_y(p); - if ( c->bounds.xmin > x ) c->bounds.xmin = x; - if ( c->bounds.ymin > y ) c->bounds.ymin = y; - if ( c->bounds.xmax < x ) c->bounds.xmax = x; - if ( c->bounds.ymax < y ) c->bounds.ymax = y; + pc_bounds_expand(&(c->bounds),p); return PC_SUCCESS; } diff --git a/lib/pc_point.c b/lib/pc_point.c index 35d539a4..56547450 100644 --- a/lib/pc_point.c +++ b/lib/pc_point.c @@ -329,6 +329,9 @@ pc_point_to_geometry_wkb(const PCPOINT *pt, size_t *wkbsize) uint32_t srid = pt->schema->srid; double x, y, z, m; + if (pt->schema->x_position < 0 || pt->schema->y_position < 0 ) + return NULL; + if ( srid != 0 ) { wkbtype |= srid_mask; @@ -379,7 +382,7 @@ pc_point_to_geometry_wkb(const PCPOINT *pt, size_t *wkbsize) if ( pt->schema->m_position > -1 ) { - m = pc_point_get_z(pt); + m = pc_point_get_m(pt); memcpy(ptr, &m, 8); /* M */ ptr += 8; } diff --git a/lib/pc_util.c b/lib/pc_util.c index fbe724df..370fde55 100644 --- a/lib/pc_util.c +++ b/lib/pc_util.c @@ -241,7 +241,11 @@ pc_bounds_intersects(const PCBOUNDS *b1, const PCBOUNDS *b2) if ( b1->xmin > b2->xmax || b1->xmax < b2->xmin || b1->ymin > b2->ymax || - b1->ymax < b2->ymin ) + b1->ymax < b2->ymin || + b1->zmin > b2->zmax || + b1->zmax < b2->zmin || + b1->mmin > b2->mmax || + b1->mmax < b2->mmin ) { return PC_FALSE; } @@ -249,17 +253,173 @@ pc_bounds_intersects(const PCBOUNDS *b1, const PCBOUNDS *b2) } void -pc_bounds_init(PCBOUNDS *b) +pc_bounds_init(PCBOUNDS *b, const PCSCHEMA *schema) { - b->xmin = b->ymin = DBL_MAX; - b->xmax = b->ymax = -1*DBL_MAX; + b->xmin = (schema->x_position == -1) ? -DBL_MAX : DBL_MAX; + b->ymin = (schema->y_position == -1) ? -DBL_MAX : DBL_MAX; + b->zmin = (schema->z_position == -1) ? -DBL_MAX : DBL_MAX; + b->mmin = (schema->m_position == -1) ? -DBL_MAX : DBL_MAX; + + b->xmax = -b->xmin; + b->ymax = -b->ymin; + b->zmax = -b->zmin; + b->mmax = -b->mmin; } void pc_bounds_merge(PCBOUNDS *b1, const PCBOUNDS *b2) { if ( b2->xmin < b1->xmin ) b1->xmin = b2->xmin; if ( b2->ymin < b1->ymin ) b1->ymin = b2->ymin; + if ( b2->zmin < b1->zmin ) b1->zmin = b2->zmin; + if ( b2->mmin < b1->mmin ) b1->mmin = b2->mmin; if ( b2->xmax > b1->xmax ) b1->xmax = b2->xmax; if ( b2->ymax > b1->ymax ) b1->ymax = b2->ymax; + if ( b2->zmax > b1->zmax ) b1->zmax = b2->zmax; + if ( b2->mmax > b1->mmax ) b1->mmax = b2->mmax; +} + +void pc_bounds_expand(PCBOUNDS *b, const PCPOINT *p) +{ + double v; + + if(p->schema->x_position > -1) + { + v = pc_point_get_x(p); + if ( b->xmin > v ) b->xmin = v; + if ( b->xmax < v ) b->xmax = v; + } + + if(p->schema->y_position > -1) + { + v = pc_point_get_y(p); + if ( b->ymin > v ) b->ymin = v; + if ( b->ymax < v ) b->ymax = v; + } + + if(p->schema->z_position > -1) + { + v = pc_point_get_z(p); + if ( b->zmin > v ) b->zmin = v; + if ( b->zmax < v ) b->zmax = v; + } + + if(p->schema->m_position > -1) + { + v = pc_point_get_m(p); + if ( b->mmin > v ) b->mmin = v; + if ( b->mmax < v ) b->mmax = v; + } +} + +uint8_t * +wkb_set_double(uint8_t *wkb, double d) +{ + memcpy(wkb, &d, 8); + wkb += 8; + return wkb; +} + +uint8_t * +wkb_set_uint32(uint8_t *wkb, uint32_t i) +{ + memcpy(wkb, &i, 4); + wkb += 4; + return wkb; +} + +uint8_t * +wkb_set_char(uint8_t *wkb, char c) +{ + memcpy(wkb, &c, 1); + wkb += 1; + return wkb; +} + +uint8_t * +pc_bounds_to_geometry_wkb(const PCBOUNDS *bounds, uint32_t srid, size_t *wkbsize) +{ + /* Bounds! */ + double xmin = bounds->xmin; + double ymin = bounds->ymin; + double xmax = bounds->xmax; + double ymax = bounds->ymax; + + static uint32_t srid_mask = 0x20000000; + static uint32_t nrings = 1; + static uint32_t npoints_by_type[] = { 0, 1, 2, 5 }; + uint32_t wkbtype = 1 + (xmin!=xmax) + (ymin!=ymax); /* WKB POINT, LINESTRING or POLYGON */ + uint32_t npoints = npoints_by_type[wkbtype]; + uint8_t *wkb, *ptr; + size_t size = 1 + wkbtype*4 + npoints*2*8; /* endian + type + (nrings?) + (npoints?) + npoints dbl pt */ + + if ( srid ) + { + wkbtype |= srid_mask; + size += 4; + } + + if ( wkbsize ) *wkbsize = size; + wkb = pcalloc(size); + ptr = wkb; + + ptr = wkb_set_char(ptr, machine_endian()); /* Endian flag */ + + ptr = wkb_set_uint32(ptr, wkbtype); /* TYPE = POINT, LINESTRING or POLYGON */ + + if ( srid ) + { + ptr = wkb_set_uint32(ptr, srid); /* SRID */ + } + + + switch( npoints ) + { + case 5 : ptr = wkb_set_uint32(ptr, nrings); /* NRINGS = 1 */ + case 2 : ptr = wkb_set_uint32(ptr, npoints); /* NPOINTS = 1, 2 or 5 */ + } + + /* Point 0 */ + ptr = wkb_set_double(ptr, xmin); + ptr = wkb_set_double(ptr, ymin); + + if(npoints==2) // LINESTRING + { + /* Point 1 */ + ptr = wkb_set_double(ptr, xmax); + ptr = wkb_set_double(ptr, ymax); + } + else if(npoints==5) // POLYGON + { + /* Point 1 */ + ptr = wkb_set_double(ptr, xmin); + ptr = wkb_set_double(ptr, ymax); + + /* Point 2 */ + ptr = wkb_set_double(ptr, xmax); + ptr = wkb_set_double(ptr, ymax); + + /* Point 3 */ + ptr = wkb_set_double(ptr, xmax); + ptr = wkb_set_double(ptr, ymin); + + /* Point 4 */ + ptr = wkb_set_double(ptr, xmin); + ptr = wkb_set_double(ptr, ymin); + } + + return wkb; } +PCBOX3D * +pc_bounds_to_box3d(const PCBOUNDS *bounds, uint32_t srid) +{ + PCBOX3D *box = (PCBOX3D *) pcalloc(sizeof(PCBOX3D)); + box->xmin = bounds->xmin; + box->ymin = bounds->ymin; + box->zmin = bounds->zmin; + box->xmax = bounds->xmax; + box->ymax = bounds->ymax; + box->zmax = bounds->zmax; + box->srid = srid; + return box; +} diff --git a/pgsql/CMakeLists.txt b/pgsql/CMakeLists.txt index ffe2e2a7..0eff01c2 100644 --- a/pgsql/CMakeLists.txt +++ b/pgsql/CMakeLists.txt @@ -10,7 +10,7 @@ set ( PC_HEADERS pc_pgsql.h ) -set ( PC_INSTALL_EXENSIONS +set ( PC_INSTALL_EXTENSIONS "${PROJECT_BINARY_DIR}/pgsql/pointcloud--${POINTCLOUD_VERSION}.sql" "${PROJECT_BINARY_DIR}/pgsql/pointcloud.control" ) @@ -64,7 +64,7 @@ install ( ) install ( - FILES ${PC_INSTALL_EXENSIONS} + FILES ${PC_INSTALL_EXTENSIONS} DESTINATION "${PGSQL_SHAREDIR}/extension" ) diff --git a/pgsql/META.json b/pgsql/META.json index f0d1b224..cf3ece5d 100644 --- a/pgsql/META.json +++ b/pgsql/META.json @@ -9,7 +9,7 @@ "provides": { "pointcloud": { "abstract": "LIDAR point and patch types and functions", - "version": "1.0.0", + "version": "1.1.0", "file": "", "docfile": "" } @@ -32,7 +32,7 @@ } }, "meta-spec": { - "version": "1.0.0", + "version": "1.1.0", "url": "http://pgxn.org/meta/spec.txt" }, "tags": [ diff --git a/pgsql/Makefile b/pgsql/Makefile index eba5329e..a9d32ae5 100644 --- a/pgsql/Makefile +++ b/pgsql/Makefile @@ -56,4 +56,4 @@ $(EXTENSION)--%--$(EXTVERSION).sql: $(EXTENSION)--$(EXTVERSION).sql ../util/proc cat $< | ../util/proc_upgrade.pl > $@ $(EXTENSION)--%--$(EXTVERSION)next.sql: $(EXTENSION)--$(EXTVERSION)next--$(EXTVERSION).sql - ln -f $< $@ + cp $< $@ diff --git a/pgsql/expected/pointcloud-ght.out b/pgsql/expected/pointcloud-ght.out index 06ea485b..172adcc4 100644 --- a/pgsql/expected/pointcloud-ght.out +++ b/pgsql/expected/pointcloud-ght.out @@ -62,7 +62,7 @@ SELECT Sum(PC_NumPoints(pa)) FROM pa_test_ght; SELECT Sum(PC_MemSize(pa)) FROM pa_test_ght; sum ----- - 582 + 710 (1 row) SELECT Sum(PC_PatchMax(pa,'x')) FROM pa_test_ght; @@ -98,7 +98,7 @@ SELECT Sum(PC_NumPoints(pa)) FROM pa_test_ght; SELECT Sum(PC_MemSize(pa)) FROM pa_test_ght; sum ------- - 38681 + 38809 (1 row) SELECT Max(PC_PatchMax(pa,'x')) FROM pa_test_ght; diff --git a/pgsql/expected/pointcloud-laz.out b/pgsql/expected/pointcloud-laz.out index ccc9bece..46d35fb2 100644 --- a/pgsql/expected/pointcloud-laz.out +++ b/pgsql/expected/pointcloud-laz.out @@ -96,7 +96,7 @@ SELECT Sum(PC_NumPoints(pa)) FROM pa_test_laz; SELECT Sum(PC_MemSize(pa)) FROM pa_test_laz; sum ----- - 487 + 615 (1 row) SELECT Sum(PC_PatchMax(pa,'x')) FROM pa_test_laz; @@ -192,7 +192,7 @@ SELECT Sum(PC_NumPoints(pa)) FROM pa_test_laz; SELECT Sum(PC_MemSize(pa)) FROM pa_test_laz; sum ------ - 1499 + 1659 (1 row) SELECT Max(PC_PatchMax(pa,'x')) FROM pa_test_laz; diff --git a/pgsql/expected/pointcloud.out b/pgsql/expected/pointcloud.out index 4fa56e28..53c4621f 100644 --- a/pgsql/expected/pointcloud.out +++ b/pgsql/expected/pointcloud.out @@ -291,10 +291,10 @@ SELECT PC_AsText(pa) FROM pa_test; {"pcid":1,"pts":[[0.06,0.07,0.05,6],[0.09,0.1,0.05,10]]} (4 rows) -SELECT PC_Envelope(pa) from pa_test; - pc_envelope +SELECT PC_Envelope_AsBinary(pa) from pa_test; + pc_envelope_asbinary ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - \x010300000001000000050000007b14ae47e17a943fb81e85eb51b89e3f7b14ae47e17a943fb81e85eb51b89e3f7b14ae47e17a943fb81e85eb51b89e3f7b14ae47e17a943fb81e85eb51b89e3f7b14ae47e17a943fb81e85eb51b89e3f + \x01010000007b14ae47e17a943fb81e85eb51b89e3f \x01030000000100000005000000b81e85eb51b8ae3fec51b81e85ebb13fb81e85eb51b8ae3f9a9999999999b93f0ad7a3703d0ab73f9a9999999999b93f0ad7a3703d0ab73fec51b81e85ebb13fb81e85eb51b8ae3fec51b81e85ebb13f \x01030000000100000005000000b81e85eb51b8ae3fec51b81e85ebb13fb81e85eb51b8ae3f9a9999999999b93f0ad7a3703d0ab73f9a9999999999b93f0ad7a3703d0ab73fec51b81e85ebb13fb81e85eb51b8ae3fec51b81e85ebb13f \x01030000000100000005000000b81e85eb51b8ae3fec51b81e85ebb13fb81e85eb51b8ae3f9a9999999999b93f0ad7a3703d0ab73f9a9999999999b93f0ad7a3703d0ab73fec51b81e85ebb13fb81e85eb51b8ae3fec51b81e85ebb13f @@ -334,7 +334,7 @@ SELECT Sum(PC_NumPoints(pa)) FROM pa_test_dim; SELECT Sum(PC_MemSize(pa)) FROM pa_test_dim; sum ----- - 684 + 812 (1 row) SELECT Sum(PC_PatchMax(pa,'x')) FROM pa_test_dim; @@ -370,7 +370,7 @@ SELECT Sum(PC_NumPoints(pa)) FROM pa_test_dim; SELECT Sum(PC_MemSize(pa)) FROM pa_test_dim; sum ------ - 8733 + 8893 (1 row) SELECT Max(PC_PatchMax(pa,'x')) FROM pa_test_dim; diff --git a/pgsql/pc_inout.c b/pgsql/pc_inout.c index 7a25cc6a..810e7aee 100644 --- a/pgsql/pc_inout.c +++ b/pgsql/pc_inout.c @@ -32,6 +32,7 @@ Datum pcpoint_as_text(PG_FUNCTION_ARGS); Datum pcpatch_as_text(PG_FUNCTION_ARGS); Datum pcpoint_as_bytea(PG_FUNCTION_ARGS); Datum pcpatch_bytea_envelope(PG_FUNCTION_ARGS); +Datum pcpatch_box3d(PG_FUNCTION_ARGS); static void @@ -293,8 +294,8 @@ Datum pcpoint_as_bytea(PG_FUNCTION_ARGS) PG_RETURN_BYTEA_P(wkb); } -PG_FUNCTION_INFO_V1(pcpatch_bytea_envelope); -Datum pcpatch_bytea_envelope(PG_FUNCTION_ARGS) +PG_FUNCTION_INFO_V1(pcpatch_envelope_as_bytea); +Datum pcpatch_envelope_as_bytea(PG_FUNCTION_ARGS) { uint8 *bytes; size_t bytes_size; @@ -303,17 +304,26 @@ Datum pcpatch_bytea_envelope(PG_FUNCTION_ARGS) SERIALIZED_PATCH *serpatch = PG_GETHEADER_SERPATCH_P(0); PCSCHEMA *schema = pc_schema_from_pcid(serpatch->pcid, fcinfo); - bytes = pc_patch_to_geometry_wkb_envelope(serpatch, schema, &bytes_size); + bytes = pc_bounds_to_geometry_wkb(&serpatch->bounds, schema->srid, &bytes_size); wkb_size = VARHDRSZ + bytes_size; wkb = palloc(wkb_size); memcpy(VARDATA(wkb), bytes, bytes_size); SET_VARSIZE(wkb, wkb_size); - pfree(bytes); + pcfree(bytes); PG_RETURN_BYTEA_P(wkb); } +PG_FUNCTION_INFO_V1(pcpatch_box3d); +Datum pcpatch_box3d(PG_FUNCTION_ARGS) +{ + SERIALIZED_PATCH *serpatch = PG_GETHEADER_SERPATCH_P(0); + PCSCHEMA *schema = pc_schema_from_pcid(serpatch->pcid, fcinfo); + PCBOX3D *box = pc_bounds_to_box3d(&serpatch->bounds, schema->srid); + PG_RETURN_POINTER(box); +} + PG_FUNCTION_INFO_V1(pc_typmod_in); Datum pc_typmod_in(PG_FUNCTION_ARGS) { diff --git a/pgsql/pc_pgsql.c b/pgsql/pc_pgsql.c index eb25c00e..6a190545 100644 --- a/pgsql/pc_pgsql.c +++ b/pgsql/pc_pgsql.c @@ -995,104 +995,3 @@ pc_patch_deserialize(const SERIALIZED_PATCH *serpatch, const PCSCHEMA *schema) pcerror("%s: unsupported compression type", __func__); return NULL; } - - -static uint8_t * -pc_patch_wkb_set_double(uint8_t *wkb, double d) -{ - memcpy(wkb, &d, 8); - wkb += 8; - return wkb; -} - -static uint8_t * -pc_patch_wkb_set_int32(uint8_t *wkb, uint32_t i) -{ - memcpy(wkb, &i, 8); - wkb += 4; - return wkb; -} - -static uint8_t * -pc_patch_wkb_set_char(uint8_t *wkb, char c) -{ - memcpy(wkb, &c, 1); - wkb += 1; - return wkb; -} - -/* 0 = xdr | big endian */ -/* 1 = ndr | little endian */ -static char -machine_endian(void) -{ - static int check_int = 1; /* dont modify this!!! */ - return *((char *) &check_int); -} - -uint8_t * -pc_patch_to_geometry_wkb_envelope(const SERIALIZED_PATCH *pa, const PCSCHEMA *schema, size_t *wkbsize) -{ - static uint32_t srid_mask = 0x20000000; - static uint32_t nrings = 1; - static uint32_t npoints = 5; - uint32_t wkbtype = 3; /* WKB POLYGON */ - uint8_t *wkb, *ptr; - int has_srid = false; - size_t size = 1 + 4 + 4 + 4 + 2*npoints*8; /* endian + type + nrings + npoints + 5 dbl pts */ - - /* Bounds! */ - double xmin = pa->bounds.xmin; - double ymin = pa->bounds.ymin; - double xmax = pa->bounds.xmax; - double ymax = pa->bounds.ymax; - - /* Make sure they're slightly bigger than a point */ - if ( xmin == xmax ) xmax += xmax * 0.0000001; - if ( ymin == ymax ) ymax += ymax * 0.0000001; - - if ( schema->srid > 0 ) - { - has_srid = true; - wkbtype |= srid_mask; - size += 4; - } - - wkb = palloc(size); - ptr = wkb; - - ptr = pc_patch_wkb_set_char(ptr, machine_endian()); /* Endian flag */ - - ptr = pc_patch_wkb_set_int32(ptr, wkbtype); /* TYPE = Polygon */ - - if ( has_srid ) - { - ptr = pc_patch_wkb_set_int32(ptr, schema->srid); /* SRID */ - } - - ptr = pc_patch_wkb_set_int32(ptr, nrings); /* NRINGS = 1 */ - ptr = pc_patch_wkb_set_int32(ptr, npoints); /* NPOINTS = 5 */ - - /* Point 0 */ - ptr = pc_patch_wkb_set_double(ptr, pa->bounds.xmin); - ptr = pc_patch_wkb_set_double(ptr, pa->bounds.ymin); - - /* Point 1 */ - ptr = pc_patch_wkb_set_double(ptr, pa->bounds.xmin); - ptr = pc_patch_wkb_set_double(ptr, pa->bounds.ymax); - - /* Point 2 */ - ptr = pc_patch_wkb_set_double(ptr, pa->bounds.xmax); - ptr = pc_patch_wkb_set_double(ptr, pa->bounds.ymax); - - /* Point 3 */ - ptr = pc_patch_wkb_set_double(ptr, pa->bounds.xmax); - ptr = pc_patch_wkb_set_double(ptr, pa->bounds.ymin); - - /* Point 4 */ - ptr = pc_patch_wkb_set_double(ptr, pa->bounds.xmin); - ptr = pc_patch_wkb_set_double(ptr, pa->bounds.ymin); - - if ( wkbsize ) *wkbsize = size; - return wkb; -} diff --git a/pgsql/pointcloud.sql.in b/pgsql/pointcloud.sql.in index f3b333b1..c2b2a05a 100644 --- a/pgsql/pointcloud.sql.in +++ b/pgsql/pointcloud.sql.in @@ -194,8 +194,8 @@ CREATE OR REPLACE FUNCTION PC_AsText(p pcpatch) RETURNS text AS 'MODULE_PATHNAME', 'pcpatch_as_text' LANGUAGE 'c' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION PC_Envelope(p pcpatch) - RETURNS bytea AS 'MODULE_PATHNAME', 'pcpatch_bytea_envelope' +CREATE OR REPLACE FUNCTION PC_Envelope_AsBinary(p pcpatch) + RETURNS bytea AS 'MODULE_PATHNAME', 'pcpatch_envelope_as_bytea' LANGUAGE 'c' IMMUTABLE STRICT; CREATE OR REPLACE FUNCTION PC_Uncompress(p pcpatch) diff --git a/pgsql/sql/pointcloud.sql b/pgsql/sql/pointcloud.sql index a0669abc..32050043 100644 --- a/pgsql/sql/pointcloud.sql +++ b/pgsql/sql/pointcloud.sql @@ -228,7 +228,7 @@ INSERT INTO pa_test (pa) VALUES ('0000000001000000000000000200000006000000070000 SELECT PC_Uncompress(pa) FROM pa_test LIMIT 1; SELECT PC_AsText(pa) FROM pa_test; -SELECT PC_Envelope(pa) from pa_test; +SELECT PC_Envelope_AsBinary(pa) from pa_test; SELECT PC_AsText(PC_Union(pa)) FROM pa_test; SELECT sum(PC_NumPoints(pa)) FROM pa_test; diff --git a/pgsql_postgis/CMakeLists.txt b/pgsql_postgis/CMakeLists.txt index 6796ca4f..68dd877c 100644 --- a/pgsql_postgis/CMakeLists.txt +++ b/pgsql_postgis/CMakeLists.txt @@ -1,11 +1,21 @@ -set ( PCPG_INSTALL_EXENSIONS - pointcloud_postgis--1.0.sql - pointcloud_postgis.control +configure_file( + pointcloud_postgis.sql.in + "${PROJECT_BINARY_DIR}/pgsql/pointcloud_postgis--${POINTCLOUD_VERSION}.sql" ) - + +configure_file( + pointcloud_postgis.control.in + "${PROJECT_BINARY_DIR}/pgsql/pointcloud_postgis.control" + ) + +set ( PCPG_INSTALL_EXTENSIONS + "${PROJECT_BINARY_DIR}/pgsql/pointcloud_postgis--${POINTCLOUD_VERSION}.sql" + "${PROJECT_BINARY_DIR}/pgsql/pointcloud_postgis.control" + ) + install ( - FILES ${PCPG_INSTALL_EXENSIONS} + FILES ${PCPG_INSTALL_EXTENSIONS} DESTINATION "${PGSQL_SHAREDIR}/extension" ) diff --git a/pgsql_postgis/META.json b/pgsql_postgis/META.json index 2154860e..1ca73d1d 100644 --- a/pgsql_postgis/META.json +++ b/pgsql_postgis/META.json @@ -2,7 +2,7 @@ "name": "pointcloud_postgis", "abstract": "PostGIS integration functions for Pointcloud", "description": "Provides GIS overlay and vector/raster hooks for point clou data.", - "version": "1.0.0", + "version": "1.1.0", "release_status": "unstable", "maintainer": "Paul Ramsey", "license": "bsd", @@ -18,7 +18,7 @@ "runtime": { "requires": { "postgis": "2.0.0", - "pointcloud": "1.0.0" + "pointcloud": "1.1.0" } } }, @@ -34,7 +34,7 @@ } }, "meta-spec": { - "version": "1.0.0", + "version": "1.1.0", "url": "http://pgxn.org/meta/spec.txt" }, "tags": [ diff --git a/pgsql_postgis/Makefile b/pgsql_postgis/Makefile index 8f17bf99..5d51208b 100644 --- a/pgsql_postgis/Makefile +++ b/pgsql_postgis/Makefile @@ -4,8 +4,11 @@ include ../config.mk #MODULE_big = pointcloud_postgis #OBJS = +SED = sed EXTENSION = pointcloud_postgis -DATA = $(EXTENSION)--1.0.sql +EXTVERSION=$(shell cat ../Version.config) +EXTVERSION_MAJOR=$(shell cut -d. -f1,2 ../Version.config) +DATA = $(EXTENSION)--$(EXTVERSION).sql #REGRESS = pointcloud @@ -15,3 +18,15 @@ DATA = $(EXTENSION)--1.0.sql # We are going to use PGXS for sure include $(PGXS) + +$(EXTENSION).control: $(EXTENSION).control.in Makefile + $(SED) -e 's/@POINTCLOUD_VERSION@/$(EXTVERSION)/' \ + -e 's/@POINTCLOUD_VERSION_MAJOR@/$(EXTVERSION_MAJOR)/' $< > $@ + @cp $(EXTENSION).sql.in $(EXTENSION)--$(EXTVERSION).sql + +$(EXTENSION)--$(EXTVERSION).sql: $(EXTENSION).sql.in Makefile + $(SED) -e 's/@POINTCLOUD_VERSION@/$(EXTVERSION)/' $< > $@ + +clean: + @rm -f $(EXTENSION)--$(EXTVERSION).sql + @rm -f $(EXTENSION).control diff --git a/pgsql_postgis/pointcloud_postgis.control b/pgsql_postgis/pointcloud_postgis.control.in similarity index 63% rename from pgsql_postgis/pointcloud_postgis.control rename to pgsql_postgis/pointcloud_postgis.control.in index 01431da0..925afc07 100644 --- a/pgsql_postgis/pointcloud_postgis.control +++ b/pgsql_postgis/pointcloud_postgis.control.in @@ -1,6 +1,7 @@ # pointcloud postgis integration extension comment = 'integration for pointcloud LIDAR data and PostGIS geometry data' -default_version = '1.0' +default_version = '@POINTCLOUD_VERSION@' +module_pathname = '$libdir/pointcloud-@POINTCLOUD_VERSION_MAJOR@' relocatable = true superuser = false requires = 'postgis, pointcloud' diff --git a/pgsql_postgis/pointcloud_postgis--1.0.sql b/pgsql_postgis/pointcloud_postgis.sql.in similarity index 59% rename from pgsql_postgis/pointcloud_postgis--1.0.sql rename to pgsql_postgis/pointcloud_postgis.sql.in index 1e6367c7..bb09a933 100644 --- a/pgsql_postgis/pointcloud_postgis--1.0.sql +++ b/pgsql_postgis/pointcloud_postgis.sql.in @@ -5,10 +5,10 @@ CREATE OR REPLACE FUNCTION PC_Intersection(pcpatch, geometry) RETURNS pcpatch AS $$ WITH - pts AS (SELECT PC_Explode($1) AS pt), - pgpts AS (SELECT ST_GeomFromEWKB(PC_AsBinary(pt)) AS pgpt, pt FROM pts), - ipts AS (SELECT pt FROM pgpts WHERE ST_Intersects(pgpt, $2)), - ipch AS (SELECT PC_Patch(pt) AS pch FROM ipts) + pts AS (SELECT PC_Explode($1) AS pt), + pgpts AS (SELECT ST_GeomFromEWKB(PC_AsBinary(pt)) AS pgpt, pt FROM pts), + ipts AS (SELECT pt FROM pgpts WHERE ST_Intersects(pgpt, $2)), + ipch AS (SELECT PC_Patch(pt) AS pch FROM ipts) SELECT pch FROM ipch; $$ LANGUAGE 'sql'; @@ -16,14 +16,14 @@ CREATE OR REPLACE FUNCTION PC_Intersection(pcpatch, geometry) ----------------------------------------------------------------------------- -- Cast from pcpatch to polygon -- -CREATE OR REPLACE FUNCTION geometry(pcpatch) +CREATE OR REPLACE FUNCTION PC_Envelope(pcpatch) RETURNS geometry AS $$ - SELECT ST_GeomFromEWKB(PC_Envelope($1)) + SELECT ST_GeomFromEWKB(PC_Envelope_AsBinary($1)) $$ LANGUAGE 'sql'; -CREATE CAST (pcpatch AS geometry) WITH FUNCTION geometry(pcpatch); +CREATE CAST (pcpatch AS geometry) WITH FUNCTION PC_Envelope(pcpatch); ----------------------------------------------------------------------------- -- Cast from pcpoint to point @@ -44,7 +44,7 @@ CREATE CAST (pcpoint AS geometry) WITH FUNCTION geometry(pcpoint); CREATE OR REPLACE FUNCTION PC_Intersects(pcpatch, geometry) RETURNS boolean AS $$ - SELECT ST_Intersects($2, geometry($1)) + SELECT ST_Intersects($2, PC_Envelope($1)) $$ LANGUAGE 'sql'; @@ -55,3 +55,13 @@ CREATE OR REPLACE FUNCTION PC_Intersects(geometry, pcpatch) $$ LANGUAGE 'sql'; +----------------------------------------------------------------------------- +-- Cast a pcpatch as a box3d +-- +CREATE OR REPLACE FUNCTION Box3D(p pcpatch) + RETURNS BOX3D AS 'MODULE_PATHNAME', 'pcpatch_box3d' + LANGUAGE 'c' IMMUTABLE STRICT; + +CREATE CAST (pcpatch AS BOX3D) WITH FUNCTION Box3D(pcpatch); + +