mirror of
https://git.osgeo.org/gitea/postgis/postgis
synced 2024-10-25 01:22:47 +00:00
b5b6d21a4e
git-svn-id: http://svn.osgeo.org/postgis/trunk@3828 b70326c6-7e19-0410-871a-916f4a2858ee
1113 lines
27 KiB
C
1113 lines
27 KiB
C
/*
|
||
* Written by Ralph Mason ralph.mason<at>telogis.com
|
||
*
|
||
* Copyright Telogis 2004
|
||
* www.telogis.com
|
||
*
|
||
* $Id$
|
||
*/
|
||
|
||
|
||
#include <string.h>
|
||
#include <math.h>
|
||
#include <stdio.h>
|
||
/* TO get byte order */
|
||
#include <sys/types.h>
|
||
#include <sys/param.h>
|
||
/* Solaris9 does not provide stdint.h */
|
||
/* #include <stdint.h> */
|
||
#include <inttypes.h>
|
||
|
||
#include "liblwgeom.h"
|
||
#include "wktparse.h"
|
||
|
||
|
||
/*-- Typedefs ---------------------------------------------- */
|
||
|
||
typedef uint32_t int4;
|
||
typedef uchar* (*outfunc)(uchar*,int);
|
||
typedef uchar* (*outwkbfunc)(uchar*);
|
||
|
||
/*-- Prototypes -------------------------------------------- */
|
||
|
||
void ensure(int chars);
|
||
void to_end(void);
|
||
void write_str(const char* str);
|
||
void write_double(double val);
|
||
void write_int(int i);
|
||
int4 read_int(uchar** geom);
|
||
double read_double(uchar** geom);
|
||
uchar* output_point(uchar* geom,int supress);
|
||
uchar* output_single(uchar* geom,int supress);
|
||
uchar* output_collection(uchar* geom,outfunc func,int supress);
|
||
uchar* output_line_collection(uchar* geom,outfunc func,int supress);
|
||
uchar* output_polygon_collection(uchar* geom,int suppress);
|
||
uchar* output_polygon_ring_collection(uchar* geom,outfunc func,int supress);
|
||
uchar* output_circstring_collection(uchar* geom,outfunc func,int supress);
|
||
uchar* output_curvepoly(uchar* geom, int supress);
|
||
uchar* output_multipoint(uchar* geom,int suppress);
|
||
uchar* output_compound(uchar* geom, int suppress);
|
||
uchar* output_multisurface(uchar* geom, int suppress);
|
||
|
||
void write_wkb_hex_bytes(uchar* ptr, unsigned int cnt, size_t size);
|
||
void write_wkb_bin_bytes(uchar* ptr, unsigned int cnt, size_t size);
|
||
void write_wkb_bin_flip_bytes(uchar* ptr, unsigned int cnt, size_t size);
|
||
void write_wkb_hex_flip_bytes(uchar* ptr, unsigned int cnt, size_t size);
|
||
|
||
void write_wkb_int(int i);
|
||
uchar* output_wkb_collection(uchar* geom,outwkbfunc func);
|
||
uchar* output_wkb_polygon_collection(uchar* geom);
|
||
uchar* output_wkb_polygon_ring_collection(uchar* geom,outwkbfunc func);
|
||
uchar* output_wkb_line_collection(uchar* geom,outwkbfunc func);
|
||
uchar* output_wkb_circstring_collection(uchar* geom,outwkbfunc func);
|
||
uchar* output_wkb_point(uchar* geom);
|
||
uchar* output_wkb(uchar* geom);
|
||
|
||
/*-- Globals ----------------------------------------------- */
|
||
|
||
static int unparser_ferror_occured;
|
||
static int dims;
|
||
static allocator local_malloc;
|
||
static freeor local_free;
|
||
static char* out_start;
|
||
static char* out_pos;
|
||
static int len;
|
||
static int lwgi;
|
||
static uchar endianbyte;
|
||
void (*write_wkb_bytes)(uchar* ptr,unsigned int cnt,size_t size);
|
||
|
||
/*
|
||
* Unparser current instance check flags - a bitmap of flags that determine which checks are enabled during the current unparse
|
||
* (see liblwgeom.h for the related PARSER_CHECK constants)
|
||
*/
|
||
int current_unparser_check_flags;
|
||
|
||
/*
|
||
* Unparser current instance result structure - the result structure being used for the current unparse
|
||
*/
|
||
LWGEOM_UNPARSER_RESULT *current_lwg_unparser_result;
|
||
|
||
/*
|
||
* Unparser error messages
|
||
*
|
||
* IMPORTANT: Make sure the order of these messages matches the UNPARSER_ERROR constants in liblwgeom.h!
|
||
* The 0th element should always be empty since it is unused (error constants start from -1)
|
||
*/
|
||
|
||
const char *unparser_error_messages[] = {
|
||
"",
|
||
"geometry requires more points",
|
||
"geometry must have an odd number of points",
|
||
"geometry contains non-closed rings"
|
||
};
|
||
|
||
/* Macro to return the error message and the current position within WKT */
|
||
#define LWGEOM_WKT_UNPARSER_ERROR(errcode) \
|
||
do { \
|
||
if (!unparser_ferror_occured) { \
|
||
unparser_ferror_occured = -1 * errcode; \
|
||
current_lwg_unparser_result->message = unparser_error_messages[errcode]; \
|
||
current_lwg_unparser_result->errlocation = (out_pos - out_start); \
|
||
} \
|
||
} while (0);
|
||
|
||
/* Macro to return the error message and the current position within WKB */
|
||
#define LWGEOM_WKB_UNPARSER_ERROR(errcode) \
|
||
do { \
|
||
if (!unparser_ferror_occured) { \
|
||
unparser_ferror_occured = -1 * errcode; \
|
||
current_lwg_unparser_result->message = unparser_error_messages[errcode]; \
|
||
current_lwg_unparser_result->errlocation = (out_pos - out_start); \
|
||
} \
|
||
} while (0);
|
||
|
||
/*---------------------------------------------------------- */
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/*
|
||
* Ensure there is enough space for chars bytes.
|
||
* Reallocate memory is this is not the case.
|
||
*/
|
||
void
|
||
ensure(int chars){
|
||
|
||
int pos = out_pos - out_start;
|
||
|
||
if ( (pos + chars) >= len ){
|
||
char* newp =(char*)local_malloc(len*2);
|
||
memcpy(newp,out_start,len);
|
||
local_free(out_start);
|
||
out_start = newp;
|
||
out_pos = newp + pos;
|
||
len *=2;
|
||
}
|
||
}
|
||
|
||
void
|
||
to_end(void)
|
||
{
|
||
while(*out_pos){
|
||
out_pos++;
|
||
}
|
||
}
|
||
|
||
void
|
||
write_str(const char* str)
|
||
{
|
||
ensure(32);
|
||
strcpy(out_pos,str);
|
||
to_end();
|
||
}
|
||
|
||
void
|
||
write_double(double val){
|
||
ensure(32);
|
||
if (lwgi)
|
||
sprintf(out_pos,"%.8g",val);
|
||
else
|
||
sprintf(out_pos,"%.15g",val);
|
||
to_end();
|
||
}
|
||
|
||
void
|
||
write_int(int i){
|
||
ensure(32);
|
||
sprintf(out_pos,"%i",i);
|
||
to_end();
|
||
}
|
||
|
||
int4
|
||
read_int(uchar** geom)
|
||
{
|
||
int4 ret;
|
||
#ifdef SHRINK_INTS
|
||
if ( getMachineEndian() == NDR ){
|
||
if( (**geom)& 0x01){
|
||
ret = **geom >>1;
|
||
(*geom)++;
|
||
return ret;
|
||
}
|
||
}
|
||
else{
|
||
if( (**geom)& 0x80){
|
||
ret = **geom & ~0x80;
|
||
(*geom)++;
|
||
return ret;
|
||
}
|
||
}
|
||
#endif
|
||
memcpy(&ret,*geom,4);
|
||
|
||
#ifdef SHRINK_INTS
|
||
if ( getMachineEndian() == NDR ){
|
||
ret >>= 1;
|
||
}
|
||
#endif
|
||
|
||
(*geom)+=4;
|
||
return ret;
|
||
}
|
||
|
||
double round(double);
|
||
|
||
double read_double(uchar** geom){
|
||
if (lwgi){
|
||
double ret = *((int4*)*geom);
|
||
ret /= 0xb60b60;
|
||
(*geom)+=4;
|
||
return ret-180.0;
|
||
}
|
||
else{
|
||
double ret;
|
||
memcpy(&ret, *geom, 8);
|
||
(*geom)+=8;
|
||
return ret;
|
||
}
|
||
}
|
||
|
||
uchar *
|
||
output_point(uchar* geom,int supress)
|
||
{
|
||
int i;
|
||
|
||
for( i = 0 ; i < dims ; i++ ){
|
||
write_double(read_double(&geom));
|
||
if (i +1 < dims )
|
||
write_str(" ");
|
||
}
|
||
return geom;
|
||
}
|
||
|
||
uchar *
|
||
output_single(uchar* geom,int supress)
|
||
{
|
||
write_str("(");
|
||
geom=output_point(geom,supress);
|
||
write_str(")");
|
||
return geom;
|
||
}
|
||
|
||
/* Output a standard collection */
|
||
uchar *
|
||
output_collection(uchar* geom,outfunc func,int supress)
|
||
{
|
||
int cnt = read_int(&geom);
|
||
if ( cnt == 0 ){
|
||
write_str(" EMPTY");
|
||
}
|
||
else{
|
||
write_str("(");
|
||
while(cnt--){
|
||
geom=func(geom,supress);
|
||
if ( cnt ){
|
||
write_str(",");
|
||
}
|
||
}
|
||
write_str(")");
|
||
}
|
||
return geom;
|
||
}
|
||
|
||
/* Output a set of LINESTRING points */
|
||
uchar *
|
||
output_line_collection(uchar* geom,outfunc func,int supress)
|
||
{
|
||
int cnt = read_int(&geom);
|
||
int orig_cnt = cnt;
|
||
|
||
if ( cnt == 0 ){
|
||
write_str(" EMPTY");
|
||
}
|
||
else{
|
||
write_str("(");
|
||
while(cnt--){
|
||
geom=func(geom,supress);
|
||
if ( cnt ){
|
||
write_str(",");
|
||
}
|
||
}
|
||
write_str(")");
|
||
}
|
||
|
||
/* Ensure that LINESTRING has a minimum of 2 points */
|
||
if ((current_unparser_check_flags & PARSER_CHECK_MINPOINTS) && orig_cnt < 2)
|
||
LWGEOM_WKT_UNPARSER_ERROR(UNPARSER_ERROR_MOREPOINTS);
|
||
|
||
return geom;
|
||
}
|
||
|
||
/* Output an individual ring from a POLYGON */
|
||
uchar *
|
||
output_polygon_ring_collection(uchar* geom,outfunc func,int supress)
|
||
{
|
||
uchar *temp;
|
||
int dimcount;
|
||
double *first_point;
|
||
double *last_point;
|
||
|
||
first_point = lwalloc(dims * sizeof(double));
|
||
last_point = lwalloc(dims * sizeof(double));
|
||
|
||
int cnt = read_int(&geom);
|
||
int orig_cnt = cnt;
|
||
if ( cnt == 0 ){
|
||
write_str(" EMPTY");
|
||
}
|
||
else{
|
||
write_str("(");
|
||
|
||
/* Store the first point of the ring (use a temp var since read_double alters
|
||
the pointer after use) */
|
||
temp = geom;
|
||
dimcount = 0;
|
||
while (dimcount < dims)
|
||
{
|
||
first_point[dimcount] = read_double(&temp);
|
||
dimcount++;
|
||
}
|
||
|
||
while(cnt--){
|
||
geom=func(geom,supress);
|
||
if ( cnt ){
|
||
write_str(",");
|
||
}
|
||
}
|
||
write_str(")");
|
||
|
||
/* Store the last point of the ring (note: we will have moved past it, so we
|
||
need to count backwards) */
|
||
temp = geom - sizeof(double) * dims;
|
||
dimcount = 0;
|
||
while (dimcount < dims)
|
||
{
|
||
last_point[dimcount] = read_double(&temp);
|
||
dimcount++;
|
||
}
|
||
|
||
/* Check if they are the same... */
|
||
if (
|
||
(first_point[0] != last_point[0] || first_point[1] != last_point[1] ) &&
|
||
(current_unparser_check_flags & PARSER_CHECK_CLOSURE))
|
||
{
|
||
lwfree(first_point);
|
||
lwfree(last_point);
|
||
LWGEOM_WKT_UNPARSER_ERROR(UNPARSER_ERROR_UNCLOSED);
|
||
}
|
||
|
||
|
||
/* Ensure that POLYGON has a minimum of 4 points */
|
||
if ((current_unparser_check_flags & PARSER_CHECK_MINPOINTS) && orig_cnt < 4)
|
||
{
|
||
lwfree(first_point);
|
||
lwfree(last_point);
|
||
LWGEOM_WKT_UNPARSER_ERROR(UNPARSER_ERROR_MOREPOINTS);
|
||
}
|
||
}
|
||
|
||
lwfree(first_point);
|
||
lwfree(last_point);
|
||
|
||
return geom;
|
||
}
|
||
|
||
/* Ouput the points from a CIRCULARSTRING */
|
||
uchar *
|
||
output_circstring_collection(uchar* geom,outfunc func,int supress)
|
||
{
|
||
int cnt = read_int(&geom);
|
||
int orig_cnt = cnt;
|
||
|
||
if ( cnt == 0 ){
|
||
write_str(" EMPTY");
|
||
}
|
||
else{
|
||
write_str("(");
|
||
while(cnt--){
|
||
geom=func(geom,supress);
|
||
if ( cnt ){
|
||
write_str(",");
|
||
}
|
||
}
|
||
write_str(")");
|
||
}
|
||
|
||
/* Ensure that a CIRCULARSTRING has a minimum of 3 points */
|
||
if ((current_unparser_check_flags & PARSER_CHECK_MINPOINTS) && orig_cnt < 3) {
|
||
LWGEOM_WKT_UNPARSER_ERROR(UNPARSER_ERROR_MOREPOINTS);
|
||
}
|
||
|
||
/* Ensure that a CIRCULARSTRING has an odd number of points */
|
||
if ((current_unparser_check_flags & PARSER_CHECK_ODD) && orig_cnt % 2 != 1) {
|
||
LWGEOM_WKT_UNPARSER_ERROR(UNPARSER_ERROR_ODDPOINTS);
|
||
}
|
||
|
||
return geom;
|
||
}
|
||
|
||
/* Output a POLYGON consisting of a number of rings */
|
||
uchar *
|
||
output_polygon_collection(uchar* geom,int suppress)
|
||
{
|
||
return output_polygon_ring_collection(geom,output_point,suppress);
|
||
}
|
||
|
||
uchar *output_wkt(uchar* geom, int supress);
|
||
|
||
/* special case for multipoint to supress extra brackets */
|
||
uchar *output_multipoint(uchar* geom,int suppress){
|
||
unsigned char type = *geom & 0x0f;
|
||
|
||
if ( type == POINTTYPE )
|
||
return output_point(++geom,suppress);
|
||
else if ( type == POINTTYPEI ){
|
||
lwgi++;
|
||
geom=output_point(++geom,0);
|
||
lwgi--;
|
||
return geom;
|
||
}
|
||
|
||
return output_wkt(geom,suppress);
|
||
}
|
||
|
||
/* Special case for compound curves: suppress the LINESTRING prefix from a curve if it appears as
|
||
a component of a COMPOUNDCURVE, but not CIRCULARSTRING */
|
||
uchar *output_compound(uchar* geom, int suppress) {
|
||
unsigned char type;
|
||
|
||
LWDEBUG(2, "output_compound called.");
|
||
|
||
type=*geom;
|
||
switch(TYPE_GETTYPE(type))
|
||
{
|
||
case LINETYPE:
|
||
geom = output_wkt(geom,2);
|
||
break;
|
||
case CIRCSTRINGTYPE:
|
||
geom = output_wkt(geom,1);
|
||
break;
|
||
}
|
||
return geom;
|
||
}
|
||
|
||
/*
|
||
* Supress linestring but not circularstring and correctly handle compoundcurve
|
||
*/
|
||
uchar *output_curvepoly(uchar* geom, int supress) {
|
||
unsigned type;
|
||
type = *geom++;
|
||
|
||
LWDEBUG(2, "output_curvepoly.");
|
||
|
||
switch(TYPE_GETTYPE(type))
|
||
{
|
||
case LINETYPE:
|
||
geom = output_collection(geom,output_point,0);
|
||
break;
|
||
case CIRCSTRINGTYPE:
|
||
write_str("CIRCULARSTRING");
|
||
geom = output_circstring_collection(geom,output_point,1);
|
||
break;
|
||
case COMPOUNDTYPE:
|
||
write_str("COMPOUNDCURVE");
|
||
geom = output_collection(geom,output_compound,1);
|
||
break;
|
||
}
|
||
return geom;
|
||
}
|
||
|
||
/* Special case for multisurfaces: suppress the POLYGON prefix from a surface if it appears as
|
||
a component of a MULTISURFACE, but not CURVEPOLYGON */
|
||
uchar *output_multisurface(uchar* geom, int suppress) {
|
||
unsigned char type;
|
||
|
||
LWDEBUG(2, "output_multisurface called.");
|
||
|
||
type=*geom;
|
||
switch(TYPE_GETTYPE(type))
|
||
{
|
||
case POLYGONTYPE:
|
||
geom = output_wkt(geom,2);
|
||
break;
|
||
case CURVEPOLYTYPE:
|
||
geom = output_wkt(geom,1);
|
||
break;
|
||
}
|
||
return geom;
|
||
}
|
||
|
||
/*
|
||
* Suppress=0 -- write TYPE, M, coords
|
||
* Suppress=1 -- write TYPE, coords
|
||
* Suppress=2 -- write only coords
|
||
*/
|
||
uchar *
|
||
output_wkt(uchar* geom, int supress)
|
||
{
|
||
|
||
unsigned char type=*geom++;
|
||
char writeM=0;
|
||
dims = TYPE_NDIMS(type); /* ((type & 0x30) >> 4)+2; */
|
||
|
||
LWDEBUG(2, "output_wkt called.");
|
||
|
||
if ( ! supress && !TYPE_HASZ(type) && TYPE_HASM(type) ) writeM=1;
|
||
|
||
|
||
/* Skip the bounding box if there is one */
|
||
if ( TYPE_HASBBOX(type) )
|
||
{
|
||
geom+=16;
|
||
}
|
||
|
||
if ( TYPE_HASSRID(type) ) {
|
||
write_str("SRID=");write_int(read_int(&geom));write_str(";");
|
||
}
|
||
|
||
switch(TYPE_GETTYPE(type)) {
|
||
case POINTTYPE:
|
||
if ( supress < 2 )
|
||
{
|
||
if (writeM) write_str("POINTM");
|
||
else write_str("POINT");
|
||
}
|
||
geom=output_single(geom,0);
|
||
break;
|
||
case LINETYPE:
|
||
if ( supress < 2 )
|
||
{
|
||
if (writeM) write_str("LINESTRINGM");
|
||
else write_str("LINESTRING");
|
||
}
|
||
geom = output_line_collection(geom,output_point,0);
|
||
break;
|
||
case CIRCSTRINGTYPE:
|
||
if ( supress < 2 )
|
||
{
|
||
if(writeM) write_str("CIRCULARSTRINGM");
|
||
else write_str("CIRCULARSTRING");
|
||
}
|
||
geom = output_circstring_collection(geom,output_point,0);
|
||
break;
|
||
case POLYGONTYPE:
|
||
if ( supress < 2 )
|
||
{
|
||
if (writeM) write_str("POLYGONM");
|
||
else write_str("POLYGON");
|
||
}
|
||
geom = output_collection(geom,output_polygon_collection,0);
|
||
break;
|
||
case COMPOUNDTYPE:
|
||
if ( supress < 2 )
|
||
{
|
||
if (writeM) write_str("COMPOUNDCURVEM");
|
||
else write_str("COMPOUNDCURVE");
|
||
}
|
||
geom = output_collection(geom, output_compound,1);
|
||
break;
|
||
case CURVEPOLYTYPE:
|
||
if (supress < 2)
|
||
{
|
||
if(writeM) write_str("CURVEPOLYGONM");
|
||
else write_str("CURVEPOLYGON");
|
||
}
|
||
geom = output_collection(geom, output_compound,0);
|
||
break;
|
||
case MULTIPOINTTYPE:
|
||
if ( supress < 2 )
|
||
{
|
||
if (writeM) write_str("MULTIPOINTM");
|
||
else write_str("MULTIPOINT");
|
||
}
|
||
geom = output_collection(geom,output_multipoint,2);
|
||
break;
|
||
case MULTILINETYPE:
|
||
if ( supress < 2 )
|
||
{
|
||
if (writeM) write_str("MULTILINESTRINGM");
|
||
else write_str("MULTILINESTRING");
|
||
}
|
||
geom = output_collection(geom,output_wkt,2);
|
||
break;
|
||
case MULTICURVETYPE:
|
||
if ( supress < 2 )
|
||
{
|
||
if (writeM) write_str("MULTICURVEM");
|
||
else write_str("MULTICURVE");
|
||
}
|
||
geom = output_collection(geom,output_compound,2);
|
||
break;
|
||
case MULTIPOLYGONTYPE:
|
||
if ( supress < 2 )
|
||
{
|
||
if (writeM) write_str("MULTIPOLYGONM");
|
||
else write_str("MULTIPOLYGON");
|
||
}
|
||
geom = output_collection(geom,output_wkt,2);
|
||
break;
|
||
case MULTISURFACETYPE:
|
||
if ( supress < 2)
|
||
{
|
||
if (writeM) write_str("MULTISURFACEM");
|
||
else write_str("MULTISURFACE");
|
||
}
|
||
geom = output_collection(geom,output_multisurface,2);
|
||
break;
|
||
case COLLECTIONTYPE:
|
||
if ( supress < 2 )
|
||
{
|
||
if (writeM) write_str("GEOMETRYCOLLECTIONM");
|
||
else write_str("GEOMETRYCOLLECTION");
|
||
}
|
||
geom = output_collection(geom,output_wkt,1);
|
||
break;
|
||
|
||
case POINTTYPEI:
|
||
if ( supress < 2 )
|
||
{
|
||
if (writeM) write_str("POINTM");
|
||
else write_str("POINT");
|
||
}
|
||
lwgi++;
|
||
geom=output_single(geom,0);
|
||
lwgi--;
|
||
break;
|
||
case LINETYPEI:
|
||
if ( supress < 2 )
|
||
{
|
||
if (writeM) write_str("LINESTRINGM");
|
||
else write_str("LINESTRING");
|
||
}
|
||
lwgi++;
|
||
geom = output_collection(geom,output_point,0);
|
||
lwgi--;
|
||
break;
|
||
case POLYGONTYPEI:
|
||
if ( supress < 2 )
|
||
{
|
||
if (writeM) write_str("POLYGONM");
|
||
else write_str("POLYGON");
|
||
}
|
||
lwgi++;
|
||
geom = output_collection(geom,output_polygon_collection,0);
|
||
lwgi--;
|
||
break;
|
||
}
|
||
return geom;
|
||
}
|
||
|
||
int
|
||
unparse_WKT(LWGEOM_UNPARSER_RESULT *lwg_unparser_result, uchar* serialized, allocator alloc, freeor free, int flags)
|
||
{
|
||
|
||
LWDEBUGF(2, "unparse_WKT called with parser flags %d.", flags);
|
||
|
||
if (serialized==NULL)
|
||
return 0;
|
||
|
||
/* Setup the inital parser flags and empty the return struct */
|
||
current_lwg_unparser_result = lwg_unparser_result;
|
||
current_unparser_check_flags = flags;
|
||
lwg_unparser_result->wkoutput = NULL;
|
||
lwg_unparser_result->size = 0;
|
||
lwg_unparser_result->serialized_lwgeom = serialized;
|
||
|
||
unparser_ferror_occured = 0;
|
||
local_malloc=alloc;
|
||
local_free=free;
|
||
len = 128;
|
||
out_start = out_pos = alloc(len);
|
||
lwgi=0;
|
||
|
||
output_wkt(serialized, 0);
|
||
|
||
/* Store the result in the struct */
|
||
lwg_unparser_result->wkoutput = out_start;
|
||
lwg_unparser_result->size = strlen(out_start);
|
||
|
||
return unparser_ferror_occured;
|
||
}
|
||
|
||
static char outchr[]={"0123456789ABCDEF" };
|
||
|
||
/* Write HEX bytes flipping */
|
||
void
|
||
write_wkb_hex_flip_bytes(uchar* ptr, unsigned int cnt, size_t size)
|
||
{
|
||
unsigned int bc; /* byte count */
|
||
|
||
ensure(cnt*2*size);
|
||
|
||
while(cnt--){
|
||
for(bc=size; bc; bc--)
|
||
{
|
||
*out_pos++ = outchr[ptr[bc-1]>>4];
|
||
*out_pos++ = outchr[ptr[bc-1]&0x0F];
|
||
}
|
||
ptr+=size;
|
||
}
|
||
}
|
||
|
||
/* Write HEX bytes w/out flipping */
|
||
void
|
||
write_wkb_hex_bytes(uchar* ptr, unsigned int cnt, size_t size)
|
||
{
|
||
unsigned int bc; /* byte count */
|
||
|
||
ensure(cnt*2*size);
|
||
|
||
while(cnt--){
|
||
for(bc=0; bc<size; bc++)
|
||
{
|
||
*out_pos++ = outchr[ptr[bc]>>4];
|
||
*out_pos++ = outchr[ptr[bc]&0x0F];
|
||
}
|
||
ptr+=size;
|
||
}
|
||
}
|
||
|
||
/* Write BIN bytes flipping */
|
||
void
|
||
write_wkb_bin_flip_bytes(uchar* ptr, unsigned int cnt, size_t size)
|
||
{
|
||
unsigned int bc; /* byte count */
|
||
|
||
ensure(cnt*size);
|
||
|
||
while(cnt--)
|
||
{
|
||
for(bc=size; bc; bc--)
|
||
*out_pos++ = ptr[bc-1];
|
||
ptr+=size;
|
||
}
|
||
}
|
||
|
||
|
||
/* Write BIN bytes w/out flipping */
|
||
void
|
||
write_wkb_bin_bytes(uchar* ptr, unsigned int cnt, size_t size)
|
||
{
|
||
unsigned int bc; /* byte count */
|
||
|
||
ensure(cnt*size);
|
||
|
||
/* Could just use a memcpy here ... */
|
||
while(cnt--)
|
||
{
|
||
for(bc=0; bc<size; bc++)
|
||
*out_pos++ = ptr[bc];
|
||
ptr+=size;
|
||
}
|
||
}
|
||
|
||
uchar *
|
||
output_wkb_point(uchar* geom)
|
||
{
|
||
if ( lwgi ){
|
||
write_wkb_bytes(geom,dims,4);
|
||
return geom + (4*dims);
|
||
}
|
||
else{
|
||
write_wkb_bytes(geom,dims,8);
|
||
return geom + (8*dims);
|
||
}
|
||
}
|
||
|
||
void
|
||
write_wkb_int(int i){
|
||
write_wkb_bytes((uchar*)&i,1,4);
|
||
}
|
||
|
||
/* Output a standard collection */
|
||
uchar *
|
||
output_wkb_collection(uchar* geom,outwkbfunc func)
|
||
{
|
||
int cnt = read_int(&geom);
|
||
|
||
LWDEBUGF(2, "output_wkb_collection: %d iterations loop", cnt);
|
||
|
||
write_wkb_int(cnt);
|
||
while(cnt--) geom=func(geom);
|
||
return geom;
|
||
}
|
||
|
||
/* Output a set of LINESTRING points */
|
||
uchar *
|
||
output_wkb_line_collection(uchar* geom,outwkbfunc func)
|
||
{
|
||
int cnt = read_int(&geom);
|
||
int orig_cnt = cnt;
|
||
|
||
LWDEBUGF(2, "output_wkb_line_collection: %d iterations loop", cnt);
|
||
|
||
write_wkb_int(cnt);
|
||
while(cnt--) geom=func(geom);
|
||
|
||
/* Ensure that LINESTRING has a minimum of 2 points */
|
||
if ((current_unparser_check_flags & PARSER_CHECK_MINPOINTS) && orig_cnt < 2) {
|
||
LWGEOM_WKB_UNPARSER_ERROR(UNPARSER_ERROR_MOREPOINTS);
|
||
}
|
||
|
||
return geom;
|
||
}
|
||
|
||
/* Output an individual ring from a POLYGON */
|
||
uchar *
|
||
output_wkb_polygon_ring_collection(uchar* geom,outwkbfunc func)
|
||
{
|
||
uchar *temp;
|
||
int dimcount;
|
||
double *first_point;
|
||
double *last_point;
|
||
|
||
first_point = lwalloc(dims * sizeof(double));
|
||
last_point = lwalloc(dims * sizeof(double));
|
||
|
||
int cnt = read_int(&geom);
|
||
int orig_cnt = cnt;
|
||
|
||
LWDEBUGF(2, "output_wkb_polygon_ring_collection: %d iterations loop", cnt);
|
||
|
||
write_wkb_int(cnt);
|
||
|
||
/* Store the first point of the ring (use a temp var since read_double alters
|
||
the pointer after use) */
|
||
temp = geom;
|
||
dimcount = 0;
|
||
while (dimcount < dims)
|
||
{
|
||
first_point[dimcount] = read_double(&temp);
|
||
dimcount++;
|
||
}
|
||
|
||
while(cnt--) geom=func(geom);
|
||
|
||
/* Store the last point of the ring (note: we will have moved past it, so we
|
||
need to count backwards) */
|
||
temp = geom - sizeof(double) * dims;
|
||
dimcount = 0;
|
||
while (dimcount < dims)
|
||
{
|
||
last_point[dimcount] = read_double(&temp);
|
||
dimcount++;
|
||
}
|
||
|
||
/* Check if they are the same... */
|
||
if (((first_point[0] != last_point[0]) ||
|
||
(first_point[1] != last_point[1])) &&
|
||
(current_unparser_check_flags & PARSER_CHECK_CLOSURE))
|
||
{
|
||
lwfree(first_point);
|
||
lwfree(last_point);
|
||
LWGEOM_WKB_UNPARSER_ERROR(UNPARSER_ERROR_UNCLOSED);
|
||
}
|
||
|
||
/* Ensure that POLYGON has a minimum of 4 points */
|
||
if ((current_unparser_check_flags & PARSER_CHECK_MINPOINTS) && orig_cnt < 4)
|
||
{
|
||
lwfree(first_point);
|
||
lwfree(last_point);
|
||
LWGEOM_WKB_UNPARSER_ERROR(UNPARSER_ERROR_MOREPOINTS);
|
||
}
|
||
|
||
lwfree(first_point);
|
||
lwfree(last_point);
|
||
|
||
return geom;
|
||
}
|
||
|
||
/* Output a POLYGON consisting of a number of rings */
|
||
uchar *
|
||
output_wkb_polygon_collection(uchar* geom)
|
||
{
|
||
LWDEBUG(2, "output_wkb_polygon_collection");
|
||
|
||
return output_wkb_polygon_ring_collection(geom,output_wkb_point);
|
||
}
|
||
|
||
/* Ouput the points from a CIRCULARSTRING */
|
||
uchar *
|
||
output_wkb_circstring_collection(uchar* geom,outwkbfunc func)
|
||
{
|
||
int cnt = read_int(&geom);
|
||
int orig_cnt = cnt;
|
||
|
||
LWDEBUGF(2, "output_wkb_curve_collection: %d iterations loop", cnt);
|
||
|
||
write_wkb_int(cnt);
|
||
while(cnt--) geom=func(geom);
|
||
|
||
/* Ensure that a CIRCULARSTRING has a minimum of 3 points */
|
||
if ((current_unparser_check_flags & PARSER_CHECK_MINPOINTS) && orig_cnt < 3) {
|
||
LWGEOM_WKB_UNPARSER_ERROR(UNPARSER_ERROR_MOREPOINTS);
|
||
}
|
||
|
||
/* Ensure that a CIRCULARSTRING has an odd number of points */
|
||
if ((current_unparser_check_flags & PARSER_CHECK_ODD) && orig_cnt % 2 != 1) {
|
||
LWGEOM_WKB_UNPARSER_ERROR(UNPARSER_ERROR_ODDPOINTS);
|
||
}
|
||
|
||
return geom;
|
||
}
|
||
|
||
uchar *
|
||
output_wkb(uchar* geom)
|
||
{
|
||
unsigned char type=*geom++;
|
||
int4 wkbtype;
|
||
|
||
dims = TYPE_NDIMS(type);
|
||
|
||
LWDEBUGF(2, "output_wkb: dims set to %d", dims);
|
||
|
||
/* Skip the bounding box */
|
||
if ( TYPE_HASBBOX(type) ) {
|
||
geom+=16;
|
||
}
|
||
|
||
wkbtype = TYPE_GETTYPE(type);
|
||
|
||
if ( TYPE_HASZ(type) )
|
||
wkbtype |= WKBZOFFSET;
|
||
if ( TYPE_HASM(type) )
|
||
wkbtype |= WKBMOFFSET;
|
||
if ( TYPE_HASSRID(type) ) {
|
||
wkbtype |= WKBSRIDFLAG;
|
||
}
|
||
|
||
|
||
/* Write byteorder (as from WKB specs...) */
|
||
write_wkb_bytes(&endianbyte,1,1);
|
||
|
||
write_wkb_int(wkbtype);
|
||
|
||
if ( TYPE_HASSRID(type) ) {
|
||
write_wkb_int(read_int(&geom));
|
||
}
|
||
|
||
switch(TYPE_GETTYPE(type)){
|
||
case POINTTYPE:
|
||
geom=output_wkb_point(geom);
|
||
break;
|
||
case LINETYPE:
|
||
geom=output_wkb_line_collection(geom,output_wkb_point);
|
||
break;
|
||
case CIRCSTRINGTYPE:
|
||
geom=output_wkb_circstring_collection(geom,output_wkb_point);
|
||
break;
|
||
case POLYGONTYPE:
|
||
geom=output_wkb_collection(geom,output_wkb_polygon_collection);
|
||
break;
|
||
case COMPOUNDTYPE:
|
||
geom=output_wkb_collection(geom,output_wkb);
|
||
break;
|
||
case CURVEPOLYTYPE:
|
||
geom=output_wkb_collection(geom,output_wkb);
|
||
break;
|
||
case MULTICURVETYPE:
|
||
case MULTISURFACETYPE:
|
||
case MULTIPOINTTYPE:
|
||
case MULTILINETYPE:
|
||
case MULTIPOLYGONTYPE:
|
||
case COLLECTIONTYPE:
|
||
geom = output_wkb_collection(geom,output_wkb);
|
||
break;
|
||
|
||
/*
|
||
These don't output standard wkb at the moment
|
||
the output and integer version.
|
||
|
||
however you could run it through the wkt parser
|
||
for a lwg and then output that. There should
|
||
also be a force_to_real_(lwgi)
|
||
*/
|
||
case POINTTYPEI:
|
||
lwgi++;
|
||
geom=output_wkb_point(geom);
|
||
lwgi--;
|
||
break;
|
||
case LINETYPEI:
|
||
lwgi++;
|
||
geom = output_wkb_collection(geom,output_wkb_point);
|
||
lwgi--;
|
||
break;
|
||
case POLYGONTYPEI:
|
||
lwgi++;
|
||
geom = output_wkb_collection(geom,output_wkb_polygon_collection);
|
||
lwgi--;
|
||
break;
|
||
}
|
||
return geom;
|
||
}
|
||
|
||
int
|
||
unparse_WKB(LWGEOM_UNPARSER_RESULT *lwg_unparser_result, uchar* serialized, allocator alloc, freeor free, int flags, char endian, uchar hex)
|
||
{
|
||
LWDEBUGF(2, "unparse_WKB(%p,...) called with parser flags %d", serialized, flags);
|
||
|
||
if (serialized==0)
|
||
return 0;
|
||
|
||
/* Setup the inital parser flags and empty the return struct */
|
||
current_lwg_unparser_result = lwg_unparser_result;
|
||
current_unparser_check_flags = flags;
|
||
lwg_unparser_result->wkoutput = NULL;
|
||
lwg_unparser_result->size = 0;
|
||
lwg_unparser_result->serialized_lwgeom = serialized;
|
||
|
||
unparser_ferror_occured = 0;
|
||
local_malloc=alloc;
|
||
local_free=free;
|
||
len = 128;
|
||
out_start = out_pos = alloc(len);
|
||
lwgi=0;
|
||
|
||
if ( endian == (char)-1 )
|
||
{
|
||
endianbyte = getMachineEndian();
|
||
if ( hex ) write_wkb_bytes = write_wkb_hex_bytes;
|
||
else write_wkb_bytes = write_wkb_bin_bytes;
|
||
}
|
||
else
|
||
{
|
||
endianbyte = endian;
|
||
if ( endianbyte != getMachineEndian() )
|
||
{
|
||
if ( hex ) write_wkb_bytes = write_wkb_hex_flip_bytes;
|
||
else write_wkb_bytes = write_wkb_bin_flip_bytes;
|
||
}
|
||
else
|
||
{
|
||
if ( hex ) write_wkb_bytes = write_wkb_hex_bytes;
|
||
else write_wkb_bytes = write_wkb_bin_bytes;
|
||
}
|
||
}
|
||
|
||
output_wkb(serialized);
|
||
|
||
if ( hex ) {
|
||
ensure(1);
|
||
*out_pos=0;
|
||
}
|
||
|
||
/* Store the result in the struct */
|
||
lwg_unparser_result->wkoutput = out_start;
|
||
lwg_unparser_result->size = (out_pos-out_start);
|
||
|
||
return unparser_ferror_occured;
|
||
}
|
||
|
||
|
||
/******************************************************************
|
||
* $Log$
|
||
* Revision 1.23 2006/02/06 11:12:22 strk
|
||
* uint32_t typedef moved back from wktparse.h to lwgparse.c and wktunparse.c
|
||
*
|
||
* Revision 1.22 2006/02/03 20:53:37 strk
|
||
* Swapped stdint.h (unavailable on Solaris9) with inttypes.h
|
||
*
|
||
* Revision 1.21 2006/02/03 09:52:14 strk
|
||
* Changed int4 typedefs to use POSIX uint32_t
|
||
*
|
||
* Revision 1.20 2006/01/09 15:12:02 strk
|
||
* ISO C90 comments
|
||
*
|
||
* Revision 1.19 2005/03/10 18:19:16 strk
|
||
* Made void args explicit to make newer compilers happy
|
||
*
|
||
* Revision 1.18 2005/02/21 16:16:14 strk
|
||
* Changed byte to uchar to avoid clashes with win32 headers.
|
||
*
|
||
* Revision 1.17 2005/02/07 13:21:10 strk
|
||
* Replaced DEBUG* macros with PGIS_DEBUG*, to avoid clashes with postgresql DEBUG
|
||
*
|
||
* Revision 1.16 2005/01/18 09:32:03 strk
|
||
* Changed unparse_WKB interface to take an output size pointer and an HEXFORM
|
||
* specifier. Reworked code in wktunparse to use function pointers.
|
||
*
|
||
* Revision 1.15 2004/12/21 15:19:01 strk
|
||
* Canonical binary reverted back to EWKB, now supporting SRID inclusion.
|
||
*
|
||
* Revision 1.14 2004/12/17 11:08:53 strk
|
||
* Moved getMachineEndian from parser to liblwgeom.{h,c}.
|
||
* Added XDR and NDR defines.
|
||
* Fixed all usage of them.
|
||
*
|
||
* Revision 1.13 2004/10/25 12:27:33 strk
|
||
* Removed useless network type includes,
|
||
* Added param.h include for BYTE_ORDER defines under win32.
|
||
*
|
||
* Revision 1.12 2004/10/21 19:48:34 strk
|
||
* Stricter syntax fixes. Reported by S<>bastien NICAISE <snicaise@iciatechnologies.com>
|
||
*
|
||
* Revision 1.11 2004/10/15 07:35:41 strk
|
||
* Fixed a bug introduced by me (byteorder skipped for inner geoms in WKB)
|
||
*
|
||
* Revision 1.10 2004/10/11 14:03:33 strk
|
||
* Added endiannes specification to unparse_WKB, AsBinary, lwgeom_to_wkb.
|
||
*
|
||
******************************************************************/
|