/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2001-2006 Refractions Research Inc. * Copyright 2017 Darafei Praliaskouski * **********************************************************************/ #include "liblwgeom_internal.h" #include "lwgeom_log.h" #include #include #include "../postgis_revision.h" #define xstr(s) str(s) #define str(s) #s const char * lwgeom_version() { static char *ptr = NULL; static char buf[256]; if ( ! ptr ) { ptr = buf; snprintf(ptr, 256, LIBLWGEOM_VERSION" " xstr(POSTGIS_REVISION)); } return ptr; } inline float next_float_down(double d) { float result; if (d > (double)FLT_MAX) return FLT_MAX; if (d <= (double)-FLT_MAX) return -FLT_MAX; result = d; if ( ((double)result) <=d ) return result; return nextafterf(result, -1*FLT_MAX); } /* * Returns the float that's very close to the input, but >=. * handles the funny differences in float4 and float8 reps. */ inline float next_float_up(double d) { float result; if (d >= (double)FLT_MAX) return FLT_MAX; if (d < (double)-FLT_MAX) return -FLT_MAX; result = d; if ( ((double)result) >=d ) return result; return nextafterf(result, FLT_MAX); } /************************************************************************ * POINTARRAY support functions * * TODO: should be moved to ptarray.c probably * ************************************************************************/ /* * Copies a point from the point array into the parameter point * will set point's z=NO_Z_VALUE if pa is 2d * will set point's m=NO_M_VALUE if pa is 3d or 2d * * NOTE: point is a real POINT3D *not* a pointer */ POINT4D getPoint4d(const POINTARRAY *pa, uint32_t n) { POINT4D result; getPoint4d_p(pa, n, &result); return result; } /* * Copies a point from the point array into the parameter point * will set point's z=NO_Z_VALUE if pa is 2d * will set point's m=NO_M_VALUE if pa is 3d or 2d * * NOTE: this will modify the point4d pointed to by 'point'. * * @return 0 on error, 1 on success */ int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *op) { uint8_t *ptr; int zmflag; if ( ! pa ) { lwerror("%s [%d] NULL POINTARRAY input", __FILE__, __LINE__); return 0; } if ( n>=pa->npoints ) { LWDEBUGF(2, "%s [%d] called with n=%d and npoints=%d", __FILE__, __LINE__, n, pa->npoints); return 0; } LWDEBUG(4, "getPoint4d_p called."); /* Get a pointer to nth point offset and zmflag */ ptr=getPoint_internal(pa, n); zmflag=FLAGS_GET_ZM(pa->flags); LWDEBUGF(4, "ptr %p, zmflag %d", ptr, zmflag); switch (zmflag) { case 0: /* 2d */ memcpy(op, ptr, sizeof(POINT2D)); op->m=NO_M_VALUE; op->z=NO_Z_VALUE; break; case 3: /* ZM */ memcpy(op, ptr, sizeof(POINT4D)); break; case 2: /* Z */ memcpy(op, ptr, sizeof(POINT3DZ)); op->m=NO_M_VALUE; break; case 1: /* M */ memcpy(op, ptr, sizeof(POINT3DM)); op->m=op->z; /* we use Z as temporary storage */ op->z=NO_Z_VALUE; break; default: lwerror("Unknown ZM flag ??"); return 0; } return 1; } /* * Copy a point from the point array into the parameter point * will set point's z=NO_Z_VALUE if pa is 2d * NOTE: point is a real POINT3DZ *not* a pointer */ POINT3DZ getPoint3dz(const POINTARRAY *pa, uint32_t n) { POINT3DZ result; getPoint3dz_p(pa, n, &result); return result; } /* * Copy a point from the point array into the parameter point * will set point's z=NO_Z_VALUE if pa is 2d * * NOTE: point is a real POINT3DZ *not* a pointer */ POINT3DM getPoint3dm(const POINTARRAY *pa, uint32_t n) { POINT3DM result; getPoint3dm_p(pa, n, &result); return result; } /* * Copy a point from the point array into the parameter point * will set point's z=NO_Z_VALUE if pa is 2d * * NOTE: this will modify the point3dz pointed to by 'point'. */ int getPoint3dz_p(const POINTARRAY *pa, uint32_t n, POINT3DZ *op) { uint8_t *ptr; if ( ! pa ) { lwerror("%s [%d] NULL POINTARRAY input", __FILE__, __LINE__); return 0; } //assert(n < pa->npoints); --causes point emtpy/point empty to crash if ( n>=pa->npoints ) { lwnotice("%s [%d] called with n=%d and npoints=%d", __FILE__, __LINE__, n, pa->npoints); return 0; } LWDEBUGF(2, "getPoint3dz_p called on array of %d-dimensions / %u pts", FLAGS_NDIMS(pa->flags), pa->npoints); /* Get a pointer to nth point offset */ ptr=getPoint_internal(pa, n); /* * if input POINTARRAY has the Z, it is always * at third position so make a single copy */ if ( FLAGS_GET_Z(pa->flags) ) { memcpy(op, ptr, sizeof(POINT3DZ)); } /* * Otherwise copy the 2d part and initialize * Z to NO_Z_VALUE */ else { memcpy(op, ptr, sizeof(POINT2D)); op->z=NO_Z_VALUE; } return 1; } /* * Copy a point from the point array into the parameter point * will set point's m=NO_Z_VALUE if pa has no M * * NOTE: this will modify the point3dm pointed to by 'point'. */ int getPoint3dm_p(const POINTARRAY *pa, uint32_t n, POINT3DM *op) { uint8_t *ptr; int zmflag; if (!pa) { lwerror("%s [%d] NULL POINTARRAY input", __FILE__, __LINE__); return LW_FALSE; } if (n >= pa->npoints) { lwerror("%s [%d] called with n=%d and npoints=%d", __FILE__, __LINE__, n, pa->npoints); return LW_FALSE; } /* Get a pointer to nth point offset and zmflag */ ptr = getPoint_internal(pa, n); zmflag = FLAGS_GET_ZM(pa->flags); /* * if input POINTARRAY has the M and NO Z, * we can issue a single memcpy */ if (zmflag == 1) { memcpy(op, ptr, sizeof(POINT3DM)); return LW_TRUE; } /* * Otherwise copy the 2d part and * initialize M to NO_M_VALUE */ memcpy(op, ptr, sizeof(POINT2D)); /* * Then, if input has Z skip it and * copy next double, otherwise initialize * M to NO_M_VALUE */ if (zmflag == 3) { ptr += sizeof(POINT3DZ); memcpy(&(op->m), ptr, sizeof(double)); } else op->m = NO_M_VALUE; return LW_TRUE; } /* * Copy a point from the point array into the parameter point * z value (if present) is not returned. * * NOTE: point is a real POINT2D *not* a pointer */ POINT2D getPoint2d(const POINTARRAY *pa, uint32_t n) { const POINT2D *result; result = getPoint2d_cp(pa, n); return *result; } /* * Copy a point from the point array into the parameter point * z value (if present) is not returned. * * NOTE: this will modify the point2d pointed to by 'point'. */ int getPoint2d_p(const POINTARRAY *pa, uint32_t n, POINT2D *point) { if ( ! pa ) { lwerror("%s [%d] NULL POINTARRAY input", __FILE__, __LINE__); return 0; } if ( n>=pa->npoints ) { lwnotice("%s [%d] called with n=%d and npoints=%d", __FILE__, __LINE__, n, pa->npoints); return 0; } /* this does x,y */ memcpy(point, getPoint_internal(pa, n), sizeof(POINT2D)); return 1; } /* * set point N to the given value * NOTE that the pointarray can be of any * dimension, the appropriate ordinate values * will be extracted from it * */ void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d) { uint8_t *ptr; assert(n < pa->npoints); ptr = getPoint_internal(pa, n); switch ( FLAGS_GET_ZM(pa->flags) ) { case 3: memcpy(ptr, p4d, sizeof(POINT4D)); break; case 2: memcpy(ptr, p4d, sizeof(POINT3DZ)); break; case 1: memcpy(ptr, p4d, sizeof(POINT2D)); ptr+=sizeof(POINT2D); memcpy(ptr, &(p4d->m), sizeof(double)); break; case 0: memcpy(ptr, p4d, sizeof(POINT2D)); break; } } void ptarray_copy_point(POINTARRAY *pa, uint32_t from, uint32_t to) { int ndims = FLAGS_NDIMS(pa->flags); switch (ndims) { case 2: { POINT2D *p_from = (POINT2D*)(getPoint_internal(pa, from)); POINT2D *p_to = (POINT2D*)(getPoint_internal(pa, to)); *p_to = *p_from; return; } case 3: { POINT3D *p_from = (POINT3D*)(getPoint_internal(pa, from)); POINT3D *p_to = (POINT3D*)(getPoint_internal(pa, to)); *p_to = *p_from; return; } case 4: { POINT4D *p_from = (POINT4D*)(getPoint_internal(pa, from)); POINT4D *p_to = (POINT4D*)(getPoint_internal(pa, to)); *p_to = *p_from; return; } default: { lwerror("%s: unsupported number of dimensions - %d", __func__, ndims); return; } } return; } /************************************************ * debugging routines ************************************************/ void printBOX3D(BOX3D *box) { lwnotice("BOX3D: %g %g, %g %g", box->xmin, box->ymin, box->xmax, box->ymax); } void printPA(POINTARRAY *pa) { uint32_t t; POINT4D pt; char *mflag; if ( FLAGS_GET_M(pa->flags) ) mflag = "M"; else mflag = ""; lwnotice(" POINTARRAY%s{", mflag); lwnotice(" ndims=%i, ptsize=%i", FLAGS_NDIMS(pa->flags), ptarray_point_size(pa)); lwnotice(" npoints = %i", pa->npoints); if (!pa) { lwnotice(" PTARRAY is null pointer!"); } else { for (t = 0; t < pa->npoints; t++) { getPoint4d_p(pa, t, &pt); if (FLAGS_NDIMS(pa->flags) == 2) lwnotice(" %i : %lf,%lf", t, pt.x, pt.y); if (FLAGS_NDIMS(pa->flags) == 3) lwnotice(" %i : %lf,%lf,%lf", t, pt.x, pt.y, pt.z); if (FLAGS_NDIMS(pa->flags) == 4) lwnotice(" %i : %lf,%lf,%lf,%lf", t, pt.x, pt.y, pt.z, pt.m); } } lwnotice(" }"); } /** * Given a string with at least 2 chars in it, convert them to * a byte value. No error checking done! */ uint8_t parse_hex(char *str) { /* do this a little brute force to make it faster */ uint8_t result_high = 0; uint8_t result_low = 0; switch (str[0]) { case '0' : result_high = 0; break; case '1' : result_high = 1; break; case '2' : result_high = 2; break; case '3' : result_high = 3; break; case '4' : result_high = 4; break; case '5' : result_high = 5; break; case '6' : result_high = 6; break; case '7' : result_high = 7; break; case '8' : result_high = 8; break; case '9' : result_high = 9; break; case 'A' : case 'a' : result_high = 10; break; case 'B' : case 'b' : result_high = 11; break; case 'C' : case 'c' : result_high = 12; break; case 'D' : case 'd' : result_high = 13; break; case 'E' : case 'e' : result_high = 14; break; case 'F' : case 'f' : result_high = 15; break; } switch (str[1]) { case '0' : result_low = 0; break; case '1' : result_low = 1; break; case '2' : result_low = 2; break; case '3' : result_low = 3; break; case '4' : result_low = 4; break; case '5' : result_low = 5; break; case '6' : result_low = 6; break; case '7' : result_low = 7; break; case '8' : result_low = 8; break; case '9' : result_low = 9; break; case 'A' : case 'a' : result_low = 10; break; case 'B' : case 'b' : result_low = 11; break; case 'C' : case 'c' : result_low = 12; break; case 'D' : case 'd' : result_low = 13; break; case 'E' : case 'e' : result_low = 14; break; case 'F' : case 'f' : result_low = 15; break; } return (uint8_t) ((result_high<<4) + result_low); } /** * Given one byte, populate result with two byte representing * the hex number. * * Ie. deparse_hex( 255, mystr) * -> mystr[0] = 'F' and mystr[1] = 'F' * * No error checking done */ void deparse_hex(uint8_t str, char *result) { int input_high; int input_low; static char outchr[]= { "0123456789ABCDEF" }; input_high = (str>>4); input_low = (str & 0x0F); result[0] = outchr[input_high]; result[1] = outchr[input_low]; } /** * Find interpolation point I * between point A and point B * so that the len(AI) == len(AB)*F * and I falls on AB segment. * * Example: * * F=0.5 : A----I----B * F=1 : A---------B==I * F=0 : A==I---------B * F=.2 : A-I-------B */ void interpolate_point4d(const POINT4D *A, const POINT4D *B, POINT4D *I, double F) { #if PARANOIA_LEVEL > 0 if (F < 0 || F > 1) lwerror("interpolate_point4d: invalid F (%g)", F); #endif I->x=A->x+((B->x-A->x)*F); I->y=A->y+((B->y-A->y)*F); I->z=A->z+((B->z-A->z)*F); I->m=A->m+((B->m-A->m)*F); } int _lwgeom_interrupt_requested = 0; void lwgeom_request_interrupt() { _lwgeom_interrupt_requested = 1; } void lwgeom_cancel_interrupt() { _lwgeom_interrupt_requested = 0; } lwinterrupt_callback *_lwgeom_interrupt_callback = 0; lwinterrupt_callback * lwgeom_register_interrupt_callback(lwinterrupt_callback *cb) { lwinterrupt_callback *old = _lwgeom_interrupt_callback; _lwgeom_interrupt_callback = cb; return old; }