/* * Written by Ralph Mason ralph.masontelogis.com * * Copyright Telogis 2004 * www.telogis.com * * $Id: wktunparse.c 2781 2008-05-22 20:43:00Z mcayland $ */ #include #include #include /* TO get byte order */ #include #include /* Solaris9 does not provide stdint.h */ /* #include */ #include #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_curve_collection(uchar* geom,outfunc func,int supress); uchar* output_collection_2(uchar* geom,int suppress); 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_curve_collection(uchar* geom,outwkbfunc func); uchar* output_wkb_point(uchar* geom); uchar* output_wkb(uchar* geom); /*-- Globals ----------------------------------------------- */ 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); /*---------------------------------------------------------- */ /* * 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); /* Ensure that LINESTRING has a minimum of 2 points */ if (cnt < 2) lwerror("geometry requires more points"); if ( cnt == 0 ){ write_str(" EMPTY"); } else{ write_str("("); while(cnt--){ geom=func(geom,supress); if ( cnt ){ write_str(","); } } write_str(")"); } 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[dims]; double last_point[dims]; int cnt = read_int(&geom); 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 (memcmp(&first_point, &last_point, sizeof(double) * dims)) lwerror("geometry contains non-closed rings"); } return geom; } /* Ouput the points from a CIRCULARSTRING */ uchar * output_curve_collection(uchar* geom,outfunc func,int supress) { int cnt = read_int(&geom); /* Ensure that a CIRCULARSTRING has a minimum of 3 points */ if (cnt < 3) lwerror("geometry requires more points"); /* Ensure that a CIRCULARSTRING has an odd number of points */ if (cnt % 2 != 1) lwerror("geometry must have an odd number of points"); 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 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 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 to suppress linestring but not circularstring */ uchar *output_compound(uchar* geom, int suppress) { unsigned type; LWDEBUG(2, "output_compound called."); type=*geom++; switch(TYPE_GETTYPE(type)) { case LINETYPE: geom = output_collection(geom,output_point,0); break; case CURVETYPE: write_str("CIRCULARSTRING"); geom = output_curve_collection(geom,output_point,1); break; } return geom; } uchar *output_multisurface(uchar* geom, int suppress) { unsigned type; LWDEBUG(2, "output_multisurface called."); type=*geom++; switch(TYPE_GETTYPE(type)) { case POLYGONTYPE: geom = output_collection(geom, output_polygon_collection,0); break; case CURVEPOLYTYPE: write_str("CURVEPOLYGON"); geom = output_collection(geom, output_compound,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 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 CURVETYPE: if ( supress < 2 ) { if(writeM) write_str("CIRCULARSTRINGM"); else write_str("CIRCULARSTRING"); } geom = output_curve_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; } char * unparse_WKT(uchar* serialized, allocator alloc, freeor free) { LWDEBUG(2, "unparse_WKT called."); if (serialized==NULL) return NULL; local_malloc=alloc; local_free=free; len = 128; out_start = out_pos = alloc(len); lwgi=0; output_wkt(serialized, 0); return out_start; } 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>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 * * 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. * ******************************************************************/