[Mapserver-users] POSTGIS connector - fixed version.
David Blasby
dblasby at refractions.net
Tue May 13 09:23:33 PDT 2003
This is a multi-part message in MIME format.
--------------030608090208000001010700
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
Mark and carl (and others)
I have modified mappostgis.c so it is compatible with postgresql 7.3,
fixes the "extra funny characters bug", plus a few more fixes. I've
attached it to this message (I've sent it to the list a few times
already). The only known problem with this is that it leaks a little
bit of memory. Several people have been beta-testing this version and
they all report that it works 'great'.
I was going to fix the memory leak and commit it to CVS when I noticed
that someone modified the mapserver 3.7 version of mappostgis.c due to
changes in the map structure (cf. map.h). Until then, the 3.6 and 3.7
versions were exactly the same - now there are two different versions.
This is causing me quite a bit of grief because my new mappostgis.c has
quite a few modifications.
So, this version works with mapserver 3.6 and
3.7-up-until-they-changed-the-map-structure.
Dave's TODO:
1. figure out what was changed in the 3.7 version
2. fix memory leak
3. make a 'unified' version that will compile under 3.6 and the new 3.7
Until then, I suggest folks use the included mappostgis.c if you are
having problems with your installation.
Please tell me if there are any problems with this version (April 14).
dave
--------------030608090208000001010700
Content-Type: text/plain;
name="mappostgis.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="mappostgis.c"
#include "map.h"
#ifndef FLT_MAX
#define FLT_MAX 25000000.0
#endif
#ifdef USE_POSTGIS
#ifndef LITTLE_ENDIAN
#define LITTLE_ENDIAN 1
#endif
#ifndef BIG_ENDIAN
#define BIG_ENDIAN 2
#endif
#include "libpq-fe.h"
#include <string.h>
typedef struct ms_POSTGIS_layer_info_t
{
char *sql; //sql query to send to DB
PGconn *conn; //connection to db
long row_num; //what row is the NEXT to be read (for random access)
PGresult *query_result;//for fetching rows from the db
char *urid_name; // name of user-specified unique identifier or OID
char *user_srid; //zero length = calculate, non-zero means using this value!
} msPOSTGISLayerInfo;
char tolower(char c)
{
if ((c <'A') || (c>'Z'))
return c;
return c-'A'+'a';
}
//remove white space
//dont send in empty strings or strings with just " " in them!
char* removeWhite(char *str)
{
int initial;
char *orig,*loc;
initial = strspn(str, " ");
if (initial != 0)
{
memmove(str, str+ initial, strlen(str) - initial+1);
}
//now final
if (strlen(str) == 0)
return str;
if (str[ strlen(str)-1] == ' ')
{
//have to remove from end
orig = str;
loc = &str[ strlen(str)-1];
while (( *loc = ' ') && (loc >orig) )
{
*loc = 0;
loc--;
}
}
return str;
}
char *strstrIgnoreCase(char *haystack, char *needle)
{
char *hay_lower;
char *needle_lower;
int len_hay,len_need;
int t;
char *loc;
len_hay = strlen(haystack);
len_need= strlen(needle);
hay_lower = (char *) malloc (len_hay +1);
needle_lower=(char*) malloc (len_need+1);
for(t=0;t<len_hay;t++)
{
hay_lower[t] = tolower(haystack[t]);
}
hay_lower[t] = 0;
for(t=0;t<len_need;t++)
{
needle_lower[t] = tolower(needle[t]);
}
needle_lower[t] =0;
loc = strstr(hay_lower,needle_lower);
free(hay_lower);
free(needle_lower);
if (loc == NULL)
{
return NULL;
}
return haystack + (loc-hay_lower);
}
//void postresql_NOTICE_HANDLER(void *arg, const char *message);
char *DATAERRORMESSAGE(char *dString, char *preamble)
{
char *m;
char tmp[7000];
m = (char*) malloc(10000);
sprintf(m,"%s",preamble);
sprintf(tmp,"Error with POSTGIS data variable. You specified '%s'.<br>\n", dString);
strcat(m,tmp);
sprintf(tmp,"Standard ways of specifiying are : <br>\n(1) 'geometry_column from geometry_table' <br>\n(2) 'geometry_column from (<sub query>) as foo using unique <column name> using SRID=<srid#>' <br><br>\n\n");
strcat(m,tmp);
sprintf(tmp,"Make sure you put in the 'using unique <column name>' and 'using SRID=#' clauses in.\n\n<br><br>");
strcat(m,tmp);
sprintf(tmp,"For more help, please see http://postgis.refractions.net/documentation.php \n\n<br><br>");
strcat(m,tmp);
sprintf(tmp,"Mappostgis.c - version of April 14/2003.\n");
strcat(m,tmp);
//printf("%s",m);
//printf("size = %i\n",strlen(m));
return m;
}
int msPOSTGISLayerParseData(char *data, char *geom_column_name,
char *table_name, char *urid_name,char *user_srid);
static int gBYTE_ORDER = 0;
//open up a connection to the postgresql database using the connection string in layer->connection
// ie. "host=192.168.50.3 user=postgres port=5555 dbname=mapserv"
int msPOSTGISLayerOpen(layerObj *layer)
{
msPOSTGISLayerInfo *layerinfo;
int order_test = 1;
//fprintf(stderr,"msPOSTGISLayerOpen called\n");
if (layer->postgislayerinfo)
return MS_SUCCESS; //already open
if( layer->data == NULL )
{
msSetError(MS_QUERYERR,
DATAERRORMESSAGE("","Error parsing POSTGIS data variable: nothing specified in DATA statement.<br><br>\n\nMore Help:<br><br>\n\n"),
"msPOSTGISLayerOpen()");
return(MS_FAILURE);
}
//have to setup a connection to the database
layerinfo = (msPOSTGISLayerInfo *) malloc( sizeof(msPOSTGISLayerInfo) );
layerinfo->sql = NULL; //calc later
layerinfo->row_num=0;
layerinfo->query_result= NULL;
layerinfo->conn = PQconnectdb( layer->connection );
if (PQstatus(layerinfo->conn) == CONNECTION_BAD)
{
msSetError(MS_QUERYERR, "couldnt make connection to DB with connect string '%s'.\n<br>\nError reported was '%s'.\n<br>\n\nThis error occured when trying to make a connection to the specified postgresql server. \n<br>\nMost commonly this is caused by <br>\n(1) incorrect connection string <br>\n(2) you didnt specify a 'user=...' in your connection string <br>\n(3) the postmaster (postgresql server) isnt running <br>\n(4) you are not allowing TCP/IP connection to the postmaster <br>\n(5) your postmaster is not running on the correct port - if its not on 5432 you must specify a 'port=...' <br>\n (6) the security on your system does not allow the webserver (usually user 'nobody') to make socket connections to the postmaster <br>\n(7) you forgot to specify a 'host=...' if the postmaster is on a different machine<br>\n(8) you made a typo <br>\n ",
"msPOSTGISLayerOpen()", layer->connection,PQerrorMessage(layerinfo->conn) );
free(layerinfo);
return(MS_FAILURE);
}
// PQsetNoticeProcessor(layerinfo->conn, postresql_NOTICE_HANDLER ,(void *) layerinfo);
layer->postgislayerinfo = (void *) layerinfo;
if( ((char *) &order_test)[0] == 1 )
gBYTE_ORDER = LITTLE_ENDIAN;
else
gBYTE_ORDER = BIG_ENDIAN;
return MS_SUCCESS;
}
// Free the itemindexes array in a layer.
void msPOSTGISLayerFreeItemInfo(layerObj *layer)
{
//fprintf(stderr,"msPOSTGISLayerFreeItemInfo called\n");
if (layer->iteminfo)
free(layer->iteminfo);
layer->iteminfo = NULL;
}
//allocate the iteminfo index array - same order as the item list
int msPOSTGISLayerInitItemInfo(layerObj *layer)
{
int i;
int *itemindexes ;
//fprintf(stderr,"msPOSTGISLayerInitItemInfo called\n");
if (layer->numitems == 0)
return MS_SUCCESS;
if (layer->iteminfo)
free(layer->iteminfo);
if((layer->iteminfo = (int *)malloc(sizeof(int)*layer->numitems))== NULL)
{
msSetError(MS_MEMERR, NULL, "msPOSTGISLayerInitItemInfo()");
return(MS_FAILURE);
}
itemindexes = (int*)layer->iteminfo;
for(i=0;i<layer->numitems;i++)
{
itemindexes[i] = i; //last one is always the geometry one - the rest are non-geom
}
return(MS_SUCCESS);
}
//Since we now have PostGIST 0.5, and 0.6 calling conventions,
// we have to attempt to handle the database in several ways. If we do the wrong
// thing, then it'll throw an error and we can rollback and try again.
//
// 2. attempt to do 0.6 calling convention (spatial ref system needed)
// 3. attempt to do 0.5 calling convention (no spatial ref system)
// The difference between 0.5 and 0.6 is that the bounding box must be
// declared to be in the same the same spatial reference system as the
// geometry column. For 0.6, we determine the SRID of the column and then
// tag the bounding box as the same SRID.
int prep_DB(char *geom_table,char *geom_column,layerObj *layer, PGresult **sql_results,rectObj rect,char *query_string, char *urid_name, char *user_srid)
{
PGresult *result;
char columns_wanted[5000];
char temp[5000];
char tmp[5000];
char tmp2[5000];
char query_string_0_6[6000];
int t;
char box3d[200];
msPOSTGISLayerInfo *layerinfo;
char *pos_from, *pos_ftab, *pos_space, *pos_paren;
char f_table_name[5000];
layerinfo = (msPOSTGISLayerInfo *) layer->postgislayerinfo;
/* Set the urid name */
layerinfo->urid_name = urid_name;
/* Extract the proper f_table_name from the geom_table string.
* We are expecting the geom_table to be either a single word
* or a sub-select clause that possibly includes a join --
*
* (select column[,column[,...]] from ftab[ natural join table2]) as foo
*
* We are expecting whitespace or a ')' after the ftab name.
*
*/
pos_from = strstr(geom_table, " from ");
if (pos_from ==NULL)
pos_from = strstr(geom_table, " FROM "); //try uppercase
if (pos_from == NULL) {
strcpy(f_table_name, geom_table);
}
else { // geom_table is a sub-select clause
pos_ftab = pos_from + 6; // This should be the start of the ftab name
pos_space = strstr(pos_ftab, " "); // First space
//pos_paren = strstr(pos_ftab, ")"); // Closing paren of clause
pos_paren = rindex(pos_ftab,')');
if ( (pos_space ==NULL) || (pos_paren ==NULL) ) {
msSetError(MS_QUERYERR,
DATAERRORMESSAGE(geom_table,"Error parsing POSTGIS data variable: Something is wrong with your subselect statement.<br><br>\n\nMore Help:<br><br>\n\n"),
"prep_DB()");
return(MS_FAILURE);
}
if (pos_paren < pos_space) { // closing parenthesis preceeds any space
strncpy(f_table_name, pos_ftab, pos_paren - pos_ftab);
}
else {
strncpy(f_table_name, pos_ftab, pos_space - pos_ftab);
}
}
if (layer->numitems ==0)
{
if (gBYTE_ORDER == LITTLE_ENDIAN)
sprintf(columns_wanted,"asbinary(force_collection(force_2d(%s)),'NDR'),%s::text", geom_column, urid_name);
else
sprintf(columns_wanted,"asbinary(force_collection(force_2d(%s)),'XDR'),%s::text", geom_column, urid_name);
}
else
{
columns_wanted[0] = 0; //len=0
for (t=0;t<layer->numitems; t++)
{
sprintf(temp,"%s::text,",layer->items[t]);
strcat(columns_wanted,temp);
}
if (gBYTE_ORDER == LITTLE_ENDIAN)
sprintf(temp,"asbinary(force_collection(force_2d(%s)),'NDR'),%s::text", geom_column, urid_name);
else
sprintf(temp,"asbinary(force_collection(force_2d(%s)),'XDR'),%s::text", geom_column, urid_name);
strcat(columns_wanted,temp);
}
sprintf(box3d,"'BOX3D(%.15g %.15g,%.15g %.15g)'::BOX3D",rect.minx, rect.miny, rect.maxx, rect.maxy);
// substitute token '!BOX!' in geom_table with the box3d - do at most 1 substitution
if (strstr(geom_table,"!BOX!"))
{
// need to do a substition
char *start, *end;
char *result;
result = malloc(7000);
start = strstr(geom_table,"!BOX!");
end = start+5;
start[0] =0;
result[0]=0;
strcat(result,geom_table);
strcat(result,box3d);
strcat(result,end);
geom_table= result;
}
if (layer->filter.string == NULL)
{
if (strlen(user_srid) == 0)
{
sprintf(query_string_0_6,"DECLARE mycursor BINARY CURSOR FOR SELECT %s from %s WHERE %s && setSRID(%s, find_srid('','%s','%s') )",
columns_wanted,geom_table,geom_column,box3d,removeWhite(f_table_name),removeWhite(geom_column));
}
else //use the user specified version
{
sprintf(query_string_0_6,"DECLARE mycursor BINARY CURSOR FOR SELECT %s from %s WHERE %s && setSRID(%s, %s )",
columns_wanted,geom_table,geom_column,box3d,user_srid);
}
}
else
{
if (strlen(user_srid) == 0)
{
sprintf(query_string_0_6,"DECLARE mycursor BINARY CURSOR FOR SELECT %s from %s WHERE (%s) and (%s && setSRID( %s,find_srid('','%s','%s') ))",
columns_wanted,geom_table,layer->filter.string,geom_column,box3d,removeWhite(f_table_name),removeWhite(geom_column));
}
else
{
sprintf(query_string_0_6,"DECLARE mycursor BINARY CURSOR FOR SELECT %s from %s WHERE (%s) and (%s && setSRID( %s,%s) )",
columns_wanted,geom_table,layer->filter.string,geom_column,box3d,user_srid);
}
}
//start transaction required by cursor
result = PQexec(layerinfo->conn, "BEGIN");
if (!(result) || PQresultStatus(result) != PGRES_COMMAND_OK)
{
msSetError(MS_QUERYERR, "Error executing POSTGIS BEGIN statement.",
"msPOSTGISLayerWhichShapes()");
PQclear(result);
layerinfo->query_result = NULL;
return(MS_FAILURE); // totally screwed
}
PQclear(result);
//set enable_seqscan=off not required (already done)
//fprintf (stderr,"query_string_0_6:%s\n",query_string_0_6);
result = PQexec(layerinfo->conn, query_string_0_6 );
if ( (result!=NULL) && (PQresultStatus(result) == PGRES_COMMAND_OK) )
{
//PQclear(result);
*sql_results = result;
strcpy(query_string, query_string_0_6 );
return (MS_SUCCESS);
}
//okay, that command didnt work. Its probably a 0.5 database
// We have to everything again, after performing a rollback.
PQclear(result);
result = PQexec(layerinfo->conn, "rollback" );
PQclear(result);
result = PQexec(layerinfo->conn, "begin" );
if (!(result) || PQresultStatus(result) != PGRES_COMMAND_OK)
{
msSetError(MS_QUERYERR, "Couldnt recover from a bad query: \n'%s'\n",
"prep_DB()",query_string_0_6);
PQclear(result);
layerinfo->query_result = NULL;
return(MS_FAILURE); // totally screwed
}
PQclear(result);
//fprintf (stderr,"prep_DB:query_string_0_5:%s\n",query_string_0_5);
sprintf(tmp2, "Error executing POSTGIS DECLARE (the actual query) statement: '%s' <br><br>\n\nPostgresql reports the error '%s'<br><br>\n\nMore Help:<br><br>\n\n",
query_string_0_6,
PQerrorMessage(layerinfo->conn)
);
sprintf(tmp, "%s%s",
tmp2,
DATAERRORMESSAGE("<check your .map file>" ,"")
);
msSetError(MS_QUERYERR,tmp,"prep_DB()");
PQclear(result);
layerinfo->query_result = NULL;
return(MS_FAILURE); // totally screwed
}
// build the neccessary SQL
// allocate a cursor for the SQL query
// get ready to read from the cursor
//
// For queries, we need to also retreive the OID for each of the rows
// So GetShape() can randomly access a row.
int msPOSTGISLayerWhichShapes(layerObj *layer, rectObj rect)
{
char *query_str;
char *table_name;
char *geom_column_name;
char *urid_name;
char *user_srid;
msPOSTGISLayerInfo *layerinfo;
int set_up_result;
table_name = malloc(500);
geom_column_name = malloc(500);
urid_name = malloc(500);
user_srid = malloc(500);
//fprintf(stderr,"msPOSTGISLayerWhichShapes called\n");
layerinfo = (msPOSTGISLayerInfo *) layer->postgislayerinfo;
if (layerinfo == NULL)
{
//layer not opened yet
msSetError(MS_QUERYERR, "msPOSTGISLayerWhichShapes called on unopened layer (layerinfo = NULL)",
"msPOSTGISLayerWhichShapes()");
return(MS_FAILURE);
}
if( layer->data == NULL )
{
msSetError(MS_QUERYERR,
"Missing DATA clause in PostGIS Layer definition. DATA statement must contain 'geometry_column from table_name' or 'geometry_column from (sub-query) as foo'.",
"msPOSTGISLayerWhichShapes()");
return(MS_FAILURE);
}
query_str = (char *) malloc(6000); //should be big enough
memset(query_str,0,6000); //zero it out
msPOSTGISLayerParseData(layer->data, geom_column_name, table_name, urid_name,user_srid);
set_up_result= prep_DB(table_name,geom_column_name, layer, &(layerinfo->query_result), rect,query_str, urid_name,user_srid);
if (set_up_result != MS_SUCCESS)
return set_up_result; //relay error
layerinfo->sql = query_str;
layerinfo->query_result = PQexec(layerinfo->conn, "FETCH ALL in mycursor");
if (!(layerinfo->query_result) || PQresultStatus(layerinfo->query_result) != PGRES_TUPLES_OK)
{
char tmp[4000];
sprintf(tmp, "Error executing POSTGIS SQL statement (in FETCH ALL): %s\n-%s\n", query_str,PQerrorMessage(layerinfo->conn) );
msSetError(MS_QUERYERR,
DATAERRORMESSAGE("",tmp),
"msPOSTGISLayerWhichShapes()");
PQclear(layerinfo->query_result);
layerinfo->query_result = NULL;
return(MS_FAILURE);
}
layerinfo->row_num =0;
return(MS_SUCCESS);
}
// Close the postgis record set and connection
int msPOSTGISLayerClose(layerObj *layer)
{
msPOSTGISLayerInfo *layerinfo;
//fprintf(stderr,"msPOSTGISLayerClose called\n");
layerinfo = (msPOSTGISLayerInfo *) layer->postgislayerinfo;
if (layerinfo != NULL)
{
PQclear(layerinfo->query_result);
layerinfo->query_result = NULL;
PQfinish(layerinfo->conn);
layerinfo->conn = NULL;
free(layerinfo);
layer->postgislayerinfo = NULL;
}
return(MS_SUCCESS);
}
//*******************************************************
// wkb is assumed to be 2d (force_2d)
// and wkb is a GEOMETRYCOLLECTION (force_collection)
// and wkb is in the endian of this computer (asbinary(...,'[XN]DR'))
// each of the sub-geom inside the collection are point,linestring, or polygon
//
// also, int is 32bits long
// double is 64bits long
//*******************************************************
// convert the wkb into points
// points -> pass through
// lines-> constituent points
// polys-> treat ring like line and pull out the consituent points
int force_to_points(char *wkb, shapeObj *shape)
{
//we're going to make a 'line' for each entity (point, line or ring) in the geom collection
int offset =0,pt_offset;
int ngeoms ;
int t,u,v;
int type,nrings,npoints;
lineObj line={0,NULL};
shape->type = MS_SHAPE_NULL; //nothing in it
memcpy( &ngeoms, &wkb[5], 4);
offset = 9; //were the first geometry is
for (t=0; t<ngeoms; t++)
{
memcpy( &type, &wkb[offset+1], 4); // type of this geometry
if (type ==1) //POINT
{
shape->type = MS_SHAPE_POINT;
line.numpoints = 1;
line.point = (pointObj *) malloc (sizeof(pointObj));
memcpy( &line.point[0].x , &wkb[offset+5 ], 8);
memcpy( &line.point[0].y , &wkb[offset+5+8], 8);
offset += 5+16;
msAddLine(shape,&line);
free(line.point);
}
if (type == 2) //linestring
{
shape->type = MS_SHAPE_POINT;
memcpy(&line.numpoints, &wkb[offset+5],4); //num points
line.point = (pointObj *) malloc (sizeof(pointObj)* line.numpoints ); //point struct
for(u=0;u<line.numpoints ; u++)
{
memcpy( &line.point[u].x , &wkb[offset+9 + (16 * u)], 8);
memcpy( &line.point[u].y , &wkb[offset+9 + (16 * u)+8], 8);
}
offset += 9 +(16)*line.numpoints; //length of object
msAddLine(shape,&line);
free(line.point);
}
if (type == 3) //polygon
{
shape->type = MS_SHAPE_POINT;
memcpy(&nrings, &wkb[offset+5],4); //num rings
//add a line for each polygon ring
pt_offset = 0;
offset += 9; //now points at 1st linear ring
for (u=0;u<nrings;u++) //for each ring, make a line
{
memcpy(&npoints, &wkb[offset],4); //num points
line.numpoints = npoints;
line.point = (pointObj *) malloc (sizeof(pointObj)* npoints); //point struct
for(v=0;v<npoints;v++)
{
memcpy( &line.point[v].x , &wkb[offset+4 + (16 * v)], 8);
memcpy( &line.point[v].y , &wkb[offset+4 + (16 * v)+8], 8);
}
//make offset point to next linear ring
msAddLine(shape,&line);
free(line.point);
offset += 4+ (16)*npoints;
}
}
}
return(MS_SUCCESS);
}
//convert the wkb into lines
// points-> remove
// lines -> pass through
// polys -> treat rings as lines
int force_to_lines(char *wkb, shapeObj *shape)
{
int offset =0,pt_offset;
int ngeoms ;
int t,u,v;
int type,nrings,npoints;
lineObj line={0,NULL};
shape->type = MS_SHAPE_NULL; //nothing in it
memcpy( &ngeoms, &wkb[5], 4);
offset = 9; //were the first geometry is
for (t=0; t<ngeoms; t++)
{
memcpy( &type, &wkb[offset+1], 4); // type of this geometry
//cannot do anything with a point
if (type == 2) //linestring
{
shape->type = MS_SHAPE_LINE;
memcpy(&line.numpoints, &wkb[offset+5],4); //num points
line.point = (pointObj *) malloc (sizeof(pointObj)* line.numpoints ); //point struct
for(u=0;u<line.numpoints ; u++)
{
memcpy( &line.point[u].x , &wkb[offset+9 + (16 * u)], 8);
memcpy( &line.point[u].y , &wkb[offset+9 + (16 * u)+8], 8);
}
offset += 9 +(16)*line.numpoints; //length of object
msAddLine(shape,&line);
free(line.point);
}
if (type == 3) //polygon
{
shape->type = MS_SHAPE_LINE;
memcpy(&nrings, &wkb[offset+5],4); //num rings
//add a line for each polygon ring
pt_offset = 0;
offset += 9; //now points at 1st linear ring
for (u=0;u<nrings;u++) //for each ring, make a line
{
memcpy(&npoints, &wkb[offset],4); //num points
line.numpoints = npoints;
line.point = (pointObj *) malloc (sizeof(pointObj)* npoints); //point struct
for(v=0;v<npoints;v++)
{
memcpy( &line.point[v].x , &wkb[offset+4 + (16 * v)], 8);
memcpy( &line.point[v].y , &wkb[offset+4 + (16 * v)+8], 8);
}
//make offset point to next linear ring
msAddLine(shape,&line);
free(line.point);
offset += 4+ (16)*npoints;
}
}
}
return(MS_SUCCESS);
}
// point -> reject
// line -> reject
// polygon -> lines of linear rings
int force_to_polygons(char *wkb, shapeObj *shape)
{
int offset =0,pt_offset;
int ngeoms ;
int t,u,v;
int type,nrings,npoints;
lineObj line={0,NULL};
shape->type = MS_SHAPE_NULL; //nothing in it
memcpy( &ngeoms, &wkb[5], 4);
offset = 9; //were the first geometry is
for (t=0; t<ngeoms; t++)
{
memcpy( &type, &wkb[offset+1], 4); // type of this geometry
if (type == 3) //polygon
{
shape->type = MS_SHAPE_POLYGON;
memcpy(&nrings, &wkb[offset+5],4); //num rings
//add a line for each polygon ring
pt_offset = 0;
offset += 9; //now points at 1st linear ring
for (u=0;u<nrings;u++) //for each ring, make a line
{
memcpy(&npoints, &wkb[offset],4); //num points
line.numpoints = npoints;
line.point = (pointObj *) malloc (sizeof(pointObj)* npoints); //point struct
for(v=0;v<npoints;v++)
{
memcpy( &line.point[v].x , &wkb[offset+4 + (16 * v)], 8);
memcpy( &line.point[v].y , &wkb[offset+4 + (16 * v)+8], 8);
}
//make offset point to next linear ring
msAddLine(shape,&line);
free(line.point);
offset += 4+ (16)*npoints;
}
}
}
return(MS_SUCCESS);
}
// if there is any polygon in wkb, return force_polygon
// if there is any line in wkb, return force_line
// otherwise return force_point
int dont_force(char *wkb, shapeObj *shape)
{
int offset =0;
int ngeoms ;
int type,t;
int best_type;
//printf("dont force");
best_type = MS_SHAPE_NULL; //nothing in it
memcpy( &ngeoms, &wkb[5], 4);
offset = 9; //were the first geometry is
for (t=0; t<ngeoms; t++)
{
memcpy( &type, &wkb[offset+1], 4); // type of this geometry
if (type == 3) //polygon
{
best_type = MS_SHAPE_POLYGON;
}
if ( (type ==2) && ( best_type != MS_SHAPE_POLYGON) )
{
best_type = MS_SHAPE_LINE;
}
if ( (type==1) && (best_type == MS_SHAPE_NULL) )
{
best_type = MS_SHAPE_POINT;
}
}
if (best_type == MS_SHAPE_POINT)
{
return force_to_points(wkb,shape);
}
if (best_type == MS_SHAPE_LINE)
{
return force_to_lines(wkb,shape);
}
if (best_type == MS_SHAPE_POLYGON)
{
return force_to_polygons(wkb,shape);
}
return(MS_FAILURE); //unknown type
}
//find the bounds of the shape
void find_bounds(shapeObj *shape)
{
int t,u;
int first_one = 1;
for (t=0; t< shape->numlines; t++)
{
for(u=0;u<shape->line[t].numpoints; u++)
{
if (first_one)
{
shape->bounds.minx = shape->line[t].point[u].x;
shape->bounds.maxx = shape->line[t].point[u].x;
shape->bounds.miny = shape->line[t].point[u].y;
shape->bounds.maxy = shape->line[t].point[u].y;
first_one = 0;
}
else
{
if (shape->line[t].point[u].x < shape->bounds.minx)
shape->bounds.minx = shape->line[t].point[u].x;
if (shape->line[t].point[u].x > shape->bounds.maxx)
shape->bounds.maxx = shape->line[t].point[u].x;
if (shape->line[t].point[u].y < shape->bounds.miny)
shape->bounds.miny = shape->line[t].point[u].y;
if (shape->line[t].point[u].y > shape->bounds.maxy)
shape->bounds.maxy = shape->line[t].point[u].y;
}
}
}
}
//find the next shape with the appropriate shape type (convert it if necessary)
// also, load in the attribute data
//MS_DONE => no more data
int msPOSTGISLayerNextShape(layerObj *layer, shapeObj *shape)
{
int result;
msPOSTGISLayerInfo *layerinfo;
layerinfo = (msPOSTGISLayerInfo *) layer->postgislayerinfo;
//fprintf(stderr,"msPOSTGISLayerNextShape called\n");
if (layerinfo == NULL)
{
msSetError(MS_QUERYERR, "NextShape called with layerinfo = NULL",
"msPOSTGISLayerNextShape()");
return(MS_FAILURE);
}
result= msPOSTGISLayerGetShapeRandom(layer, shape, &(layerinfo->row_num) );
// getshaperandom will increment the row_num
//layerinfo->row_num ++;
return result;
}
//Used by NextShape() to access a shape in the query set
// TODO: only fetch 1000 rows at a time. This should check to see if the
// requested feature is in the set. If it is, return it, otherwise
// grab the next 1000 rows.
int msPOSTGISLayerGetShapeRandom(layerObj *layer, shapeObj *shape, long *record)
{
msPOSTGISLayerInfo *layerinfo;
char *wkb;
int result,t,size;
char *temp,*temp2;
long record_oid;
layerinfo = (msPOSTGISLayerInfo *) layer->postgislayerinfo;
//fprintf(stderr,"msPOSTGISLayerGetShapeRandom : called row %li\n",record);
if (layerinfo == NULL)
{
msSetError(MS_QUERYERR, "GetShape called with layerinfo = NULL",
"msPOSTGISLayerGetShape()");
return(MS_FAILURE);
}
if (layerinfo->conn == NULL)
{
msSetError(MS_QUERYERR, "NextShape called on POSTGIS layer with no connection to DB.",
"msPOSTGISLayerGetShape()");
return(MS_FAILURE);
}
if (layerinfo->query_result == NULL)
{
msSetError(MS_QUERYERR, "GetShape called on POSTGIS layer with invalid DB query results.",
"msPOSTGISLayerGetShapeRandom()");
return(MS_FAILURE);
}
shape->type = MS_SHAPE_NULL;
while(shape->type == MS_SHAPE_NULL)
{
if ( (*record) < PQntuples(layerinfo->query_result) )
{
//retreive an item
wkb = (char *) PQgetvalue(layerinfo->query_result, (*record), layer->numitems);
switch(layer->type)
{
case MS_LAYER_POINT:
result = force_to_points(wkb, shape);
break;
case MS_LAYER_LINE:
result = force_to_lines(wkb, shape);
break;
case MS_LAYER_POLYGON:
result = force_to_polygons(wkb, shape);
break;
case MS_LAYER_ANNOTATION:
case MS_LAYER_QUERY:
result = dont_force(wkb,shape);
break;
case MS_LAYER_RASTER:
msDebug( "Ignoring MS_LAYER_RASTER in mappostgis.c\n" );
break;
case MS_LAYER_CIRCLE:
msDebug( "Ignoring MS_LAYER_RASTER in mappostgis.c\n" );
break;
}
if (shape->type != MS_SHAPE_NULL)
{
//have to retreive the attributes
shape->values = (char **) malloc(sizeof(char *) * layer->numitems);
for (t=0;t<layer->numitems;t++)
{
temp = (char *) PQgetvalue(layerinfo->query_result, (*record), t);
size = PQgetlength(layerinfo->query_result,(*record), t ) ;
temp2 = (char *) malloc(size+1 );
memcpy(temp2, temp, size);
temp2[size] = 0; //null terminate it
shape->values[t] = temp2;
}
temp = (char *) PQgetvalue(layerinfo->query_result, (*record), t+1); // t is WKB, t+1 is OID
record_oid = strtol (temp,NULL,10);
shape->index = record_oid;
shape->numvalues = layer->numitems;
find_bounds(shape);
(*record)++; //move to next shape
return (MS_SUCCESS);
}
else
{
(*record)++; //move to next shape
}
}
else
{
return (MS_DONE);
}
}
msFreeShape(shape);
return(MS_FAILURE);
}
// Execute a query on the DB based on record being an OID.
int msPOSTGISLayerGetShape(layerObj *layer, shapeObj *shape, long record)
{
char *query_str;
char table_name[5000];
char geom_column_name[5000];
char urid_name[5000];
char user_srid[5000];
//int nitems;
char columns_wanted[5000];
char temp[5000];
PGresult *query_result;
msPOSTGISLayerInfo *layerinfo;
char *wkb;
int result,t,size;
char *temp1,*temp2;
//fprintf(stderr,"msPOSTGISLayerGetShape called for record = %i\n",record);
layerinfo = (msPOSTGISLayerInfo *) layer->postgislayerinfo;
if (layerinfo == NULL)
{
//layer not opened yet
msSetError(MS_QUERYERR, "msPOSTGISLayerGetShape called on unopened layer (layerinfo = NULL)",
"msPOSTGISLayerGetShape()");
return(MS_FAILURE);
}
query_str = (char *) malloc(6000); //should be big enough
memset(query_str,0,6000); //zero it out
msPOSTGISLayerParseData(layer->data, geom_column_name, table_name, urid_name,user_srid);
if (layer->numitems ==0) //dont need the oid since its really record
{
if (gBYTE_ORDER == LITTLE_ENDIAN)
sprintf(columns_wanted,"asbinary(force_collection(force_2d(%s)),'NDR')", geom_column_name);
else
sprintf(columns_wanted,"asbinary(force_collection(force_2d(%s)),'XDR')", geom_column_name);
}
else
{
columns_wanted[0] = 0; //len=0
for (t=0;t<layer->numitems; t++)
{
sprintf(temp,"%s::text,",layer->items[t]);
strcat(columns_wanted,temp);
}
if (gBYTE_ORDER == LITTLE_ENDIAN)
sprintf(temp,"asbinary(force_collection(force_2d(%s)),'NDR')", geom_column_name);
else
sprintf(temp,"asbinary(force_collection(force_2d(%s)),'XDR')", geom_column_name);
strcat(columns_wanted,temp);
}
sprintf(query_str,"DECLARE mycursor BINARY CURSOR FOR SELECT %s from %s WHERE %s = %li", columns_wanted,table_name,urid_name,record);
//fprintf(stderr,"msPOSTGISLayerGetShape: %s \n",query_str);
query_result = PQexec(layerinfo->conn, "BEGIN");
if (!(query_result) || PQresultStatus(query_result) != PGRES_COMMAND_OK)
{
msSetError(MS_QUERYERR, "Error executing POSTGIS BEGIN statement.",
"msPOSTGISLayerGetShape()");
PQclear(query_result);
query_result = NULL;
return(MS_FAILURE);
}
query_result = PQexec(layerinfo->conn, "set enable_seqscan = off");
if (!(query_result) || PQresultStatus(query_result) != PGRES_COMMAND_OK)
{
msSetError(MS_QUERYERR, "Error executing POSTGIS 'set enable_seqscan off' statement.",
"msPOSTGISLayerGetShape()");
PQclear(query_result);
query_result = NULL;
return(MS_FAILURE);
}
PQclear(query_result);
query_result = PQexec(layerinfo->conn, query_str );
if (!(query_result) || PQresultStatus(query_result) != PGRES_COMMAND_OK)
{
char tmp[4000];
sprintf(tmp, "Error executing POSTGIS SQL statement (in FETCH ALL): %s\n-%s\n<br>More Help:<br>", query_str,PQerrorMessage(layerinfo->conn) );
msSetError(MS_QUERYERR,
DATAERRORMESSAGE("",tmp),
"msPOSTGISLayerGetShape()");
PQclear(query_result);
query_result = NULL;
return(MS_FAILURE);
}
PQclear(query_result);
query_result = PQexec(layerinfo->conn, "FETCH ALL in mycursor");
if (!(query_result) || PQresultStatus(query_result) != PGRES_TUPLES_OK)
{
char tmp[4000];
sprintf(tmp, "Error executing POSTGIS SQL statement (in FETCH ALL): %s\n-%s\n", query_str,PQerrorMessage(layerinfo->conn) );
msSetError(MS_QUERYERR,
DATAERRORMESSAGE("",tmp),
"msPOSTGISLayerGetShape()");
PQclear(query_result);
query_result = NULL;
return(MS_FAILURE);
}
//query has been done, so we can retreive the results
shape->type = MS_SHAPE_NULL;
if ( 0 < PQntuples(query_result) ) //only need to get one shape
{
//retreive an item
wkb = (char *) PQgetvalue(query_result, 0, layer->numitems); // layer->numitems is the wkt column
switch(layer->type)
{
case MS_LAYER_POINT:
result = force_to_points(wkb, shape);
break;
case MS_LAYER_LINE:
result = force_to_lines(wkb, shape);
break;
case MS_LAYER_POLYGON:
result = force_to_polygons(wkb, shape);
break;
case MS_LAYER_ANNOTATION:
case MS_LAYER_QUERY:
result = dont_force(wkb,shape);
break;
case MS_LAYER_RASTER:
msDebug( "Ignoring MS_LAYER_RASTER in mappostgis.c\n" );
break;
case MS_LAYER_CIRCLE:
msDebug( "Ignoring MS_LAYER_RASTER in mappostgis.c\n" );
}
if (shape->type != MS_SHAPE_NULL)
{
//have to retreive the attributes
shape->values = (char **) malloc(sizeof(char *) * layer->numitems);
for (t=0;t<layer->numitems;t++)
{
//fprintf(stderr,"msPOSTGISLayerGetShape: finding attribute info for '%s'\n",layer->items[t]);
temp1= (char *) PQgetvalue(query_result, 0, t);
size = PQgetlength(query_result,0, t ) ;
temp2 = (char *) malloc(size+1 );
memcpy(temp2, temp1, size);
temp2[size] = 0; //null terminate it
shape->values[t] = temp2;
//fprintf(stderr,"msPOSTGISLayerGetShape: shape->values[%i] has value '%s'\n",t,shape->values[t]);
}
shape->index = record;
shape->numvalues = layer->numitems;
find_bounds(shape);
return (MS_SUCCESS);
}
}
else
{
return (MS_DONE);
}
msFreeShape(shape);
return(MS_FAILURE);
}
//query the DB for info about the requested table
//
// CHEAT: dont look in the system tables, get query optimization infomation
//
// get the table name, return a list of the possible columns (except GEOMETRY column)
//
// found out this is called during a query
int msPOSTGISLayerGetItems(layerObj *layer)
{
msPOSTGISLayerInfo *layerinfo;
char table_name[5000];
char geom_column_name[5000];
char urid_name[5000];
char user_srid[5000];
char sql[6000];
//int nitems;
PGresult *query_result;
int t;
char *col;
char found_geom = 0;
int item_num;
//fprintf(stderr, "in msPOSTGISLayerGetItems (find column names)\n");
layerinfo = (msPOSTGISLayerInfo *) layer->postgislayerinfo;
if (layerinfo == NULL)
{
//layer not opened yet
msSetError(MS_QUERYERR, "msPOSTGISLayerGetItems called on unopened layer",
"msPOSTGISLayerGetItems()");
return(MS_FAILURE);
}
if (layerinfo->conn == NULL)
{
msSetError(MS_QUERYERR, "msPOSTGISLayerGetItems called on POSTGIS layer with no connection to DB.",
"msPOSTGISLayerGetItems()");
return(MS_FAILURE);
}
//get the table name and geometry column name
msPOSTGISLayerParseData(layer->data, geom_column_name, table_name, urid_name, user_srid);
// two cases here. One, its a table (use select * from table) otherwise, just use the select clause
sprintf(sql,"SELECT * FROM %s LIMIT 0",table_name); // attempt the query, but dont actually do much (this might take some time if there is an order by!)
query_result = PQexec(layerinfo->conn, sql );
if (!(query_result) || PQresultStatus(query_result) != PGRES_TUPLES_OK)
{
char tmp[4000];
sprintf(tmp, "Error executing POSTGIS SQL statement (in msPOSTGISLayerGetItems): %s\n-%s\n", sql,PQerrorMessage(layerinfo->conn) );
msSetError(MS_QUERYERR,
DATAERRORMESSAGE("",tmp),
"msPOSTGISLayerGetItems()");
PQclear(query_result);
query_result = NULL;
return(MS_FAILURE);
}
layer->numitems = PQnfields(query_result)-1; //dont include the geometry column
layer->items = malloc (sizeof(char *) * (layer->numitems+1) ); // +1 incase there is a problem finding goeometry column
// it will return an error if there is no geometry column found,
// so this isnt a problem
found_geom = 0; //havent found the geom field
item_num = 0;
for (t=0;t<PQnfields(query_result);t++)
{
col = PQfname(query_result,t);
if (strcmp(col, geom_column_name) != 0) // this isnt the geometry column
{
layer->items[item_num] = (char*)malloc(strlen(col)+1);
strcpy(layer->items[item_num], col);
item_num++;
}
else
{
found_geom = 1;
}
}
PQclear(query_result);
query_result = NULL;
if (!(found_geom))
{
char tmp[4000];
sprintf(tmp, "msPOSTGISLayerGetItems: tried to find the geometry column in the results from the database, but couldnt find it. Is it miss-capitialized? '%s'", geom_column_name );
msSetError(MS_QUERYERR,
tmp,
"msPOSTGISLayerGetItems()");
PQclear(query_result);
query_result = NULL;
return(MS_FAILURE);
}
return msPOSTGISLayerInitItemInfo(layer);
}
//we return an infinite extent
// we could call the SQL AGGREGATE extent(GEOMETRY), but that would take FOREVER
// to return (it has to read the entire table).
// So, we just tell it that we're everywhere and lets the spatial indexing figure things out for us
//
// Never seen this function actually called
int msPOSTGISLayerGetExtent(layerObj *layer, rectObj *extent)
{
//fprintf(stderr,"msPOSTGISLayerGetExtent called\n");
extent->minx = extent->miny = -1.0*FLT_MAX ;
extent->maxx = extent->maxy = FLT_MAX;
return(MS_SUCCESS);
//this should get the real extents,but it requires a table read
// unforunately, there is no way to call this function from mapscript, so its
// pretty useless. Untested since you cannot actually call it.
/*
PGresult *query_result;
char sql[5000];
msPOSTGISLayerInfo *layerinfo;
char table_name[5000];
char geom_column_name[5000];
char urid_name[5000];
char user_srid[5000];
if (layer == NULL)
{
char tmp[5000];
sprintf(tmp, "layer is null - have you opened the layer yet?");
msSetError(MS_QUERYERR, tmp,
"msPOSTGISLayerGetExtent()");
return(MS_FAILURE);
}
layerinfo = (msPOSTGISLayerInfo *) layer->postgislayerinfo;
msPOSTGISLayerParseData(layer->data, geom_column_name,table_name, urid_name,user_srid);
sprintf(sql,"select extent(%s) from %s", geom_column_name,table_name);
if (layerinfo->conn == NULL)
{
char tmp[5000];
sprintf(tmp, "layer doesnt have a postgis connection - have you opened the layer yet?");
msSetError(MS_QUERYERR, tmp,
"msPOSTGISLayerGetExtent()");
return(MS_FAILURE);
}
query_result = PQexec(layerinfo->conn, sql);
if (!(query_result) || PQresultStatus(query_result) != PGRES_TUPLES_OK)
{
char tmp[5000];
sprintf(tmp, "Error executing POSTGIS SQL statement (in msPOSTGISLayerGetExtent): %s", layerinfo->sql);
msSetError(MS_QUERYERR, tmp,
"msPOSTGISLayerGetExtent()");
PQclear(query_result);
return(MS_FAILURE);
}
if (PQntuples(query_result) != 1)
{
char tmp[5000];
sprintf(tmp, "Error executing POSTGIS SQL statement (in msPOSTGISLayerGetExtent) [doesnt have exactly 1 result]: %s", layerinfo->sql);
msSetError(MS_QUERYERR, tmp,
"msPOSTGISLayerGetExtent()");
PQclear(query_result);
return(MS_FAILURE);
}
sscanf(PQgetvalue(query_result,0,0),"%lf %lf %lf %lf", &extent->minx,&extent->miny,&extent->maxx,&extent->maxy );
PQclear(query_result);
*/
}
/* Function to parse the Mapserver DATA parameter for geometry
* column name, table name and name of a column to serve as a
* unique record id
*/
int msPOSTGISLayerParseData(char *data, char *geom_column_name,
char *table_name, char *urid_name,char *user_srid)
{
char *pos_opt, *pos_scn, *tmp, *pos_srid;
int slength;
/* given a string of the from 'geom from ctivalues' or 'geom from () as foo'
* return geom_column_name as 'geom'
* and table name as 'ctivalues' or 'geom from () as foo'
*/
/* First look for the optional ' using unique ID' string */
pos_opt = strstrIgnoreCase(data, " using unique ");
if (pos_opt == NULL) {
/* No user specified unique id so we will use the Postgesql OID */
strcpy(urid_name, "OID");
}
else {
// CHANGE - protect the trailing edge for thing like 'using unique ftab_id using srid=33'
tmp = strstr(pos_opt + 14," ");
if (tmp == NULL) //it lookes like 'using unique ftab_id'
{
strcpy(urid_name, pos_opt + 14);
}
else
{
//looks like ' using unique ftab_id ' (space at end)
strncpy(urid_name, pos_opt + 14, tmp-(pos_opt + 14 ) );
urid_name[tmp-(pos_opt + 14)] = 0; // null terminate it
}
}
pos_srid = strstrIgnoreCase(data," using SRID=");
if (pos_srid == NULL)
{
user_srid[0] = 0; // = ""
}
else
{
//find the srid
slength=strspn(pos_srid+12,"-0123456789");
if (slength == 0)
{
msSetError(MS_QUERYERR,
DATAERRORMESSAGE(data,"Error parsing POSTGIS data variable: You specified 'using SRID=#' but didnt have any numbers!<br><br>\n\nMore Help:<br><br>\n\n"),
"msPOSTGISLayerParseData()");
return(MS_FAILURE);
}
else
{
strncpy(user_srid,pos_srid+12,slength);
user_srid[slength] = 0; // null terminate it
}
}
// this is a little hack so the rest of the code works. If the ' using SRID=' comes before
// the ' using unique ', then make sure pos_opt points to where the ' using SRID' starts!
if (pos_opt == NULL)
{
pos_opt = pos_srid;
}
else
{
if (pos_srid != NULL)
{
if (pos_opt>pos_srid)
pos_opt = pos_srid;
}
}
/* Scan for the table or sub-select clause */
pos_scn = strstr(data, " from ");
if (pos_scn == NULL) {
msSetError(MS_QUERYERR,
DATAERRORMESSAGE(data,"Error parsing POSTGIS data variable. Must contain 'geometry_column from table_name' or 'geom from (subselect) as foo' (couldnt find ' from '). More help: <br><br>\n\n"),
"msPOSTGISLayerParseData()");
//msSetError(MS_QUERYERR, "Error parsing POSTGIS data variable. Must contain 'geometry_column from table_name' or 'geom from (subselect) as foo' (couldnt find ' from ').", "msPOSTGISLayerParseData()");
return(MS_FAILURE);
}
/* Copy the geometry column name */
memcpy(geom_column_name, data, (pos_scn)-(data));
geom_column_name[(pos_scn)-(data)] = 0; //null terminate it
/* Copy out the table name or sub-select clause */
if (pos_opt == NULL) {
strcpy(table_name, pos_scn + 6); //table name or sub-select clause
}
else {
strncpy(table_name, pos_scn + 6, (pos_opt) - (pos_scn + 6));
table_name[(pos_opt) - (pos_scn + 6)] = 0; //null terminate it
}
if ( (strlen(table_name) < 1 ) || (strlen(geom_column_name) < 1 ) ) {
msSetError(MS_QUERYERR,
DATAERRORMESSAGE(data,"Error parsing POSTGIS data variable. Must contain 'geometry_column from table_name' or 'geom from (subselect) as foo' (couldnt find a geometry_column or table/subselect). More help: <br><br>\n\n"),
"msPOSTGISLayerParseData()");
return(MS_FAILURE);
}
//printf("msPOSTGISLayerParseData: unique column = %s, srid='%s', geom_column_name = %s, table_name=%s\n", urid_name,user_srid,geom_column_name,table_name);
return(MS_SUCCESS);
}
#else
//prototypes if postgis isnt supposed to be compiled
int msPOSTGISLayerOpen(layerObj *layer)
{
msSetError(MS_QUERYERR, "msPOSTGISLayerOpen called but unimplemented! (mapserver not compiled with postgis support)",
"msPOSTGISLayerOpen()");
return(MS_FAILURE);
}
void msPOSTGISLayerFreeItemInfo(layerObj *layer)
{
msSetError(MS_QUERYERR, "msPOSTGISLayerFreeItemInfo called but unimplemented!(mapserver not compiled with postgis support)",
"msPOSTGISLayerFreeItemInfo()");
}
int msPOSTGISLayerInitItemInfo(layerObj *layer)
{
msSetError(MS_QUERYERR, "msPOSTGISLayerInitItemInfo called but unimplemented!(mapserver not compiled with postgis support)",
"msPOSTGISLayerInitItemInfo()");
return(MS_FAILURE);
}
int msPOSTGISLayerWhichShapes(layerObj *layer, rectObj rect)
{
msSetError(MS_QUERYERR, "msPOSTGISLayerWhichShapes called but unimplemented!(mapserver not compiled with postgis support)",
"msPOSTGISLayerWhichShapes()");
return(MS_FAILURE);
}
int msPOSTGISLayerClose(layerObj *layer)
{
msSetError(MS_QUERYERR, "msPOSTGISLayerClose called but unimplemented!(mapserver not compiled with postgis support)",
"msPOSTGISLayerClose()");
return(MS_FAILURE);
}
int msPOSTGISLayerNextShape(layerObj *layer, shapeObj *shape)
{
msSetError(MS_QUERYERR, "msPOSTGISLayerNextShape called but unimplemented!(mapserver not compiled with postgis support)",
"msPOSTGISLayerNextShape()");
return(MS_FAILURE);
}
int msPOSTGISLayerGetShape(layerObj *layer, shapeObj *shape, long record)
{
msSetError(MS_QUERYERR, "msPOSTGISLayerGetShape called but unimplemented!(mapserver not compiled with postgis support)",
"msPOSTGISLayerGetShape()");
return(MS_FAILURE);
}
int msPOSTGISLayerGetExtent(layerObj *layer, rectObj *extent)
{
msSetError(MS_QUERYERR, "msPOSTGISLayerGetExtent called but unimplemented!(mapserver not compiled with postgis support)",
"msPOSTGISLayerGetExtent()");
return(MS_FAILURE);
}
int msPOSTGISLayerGetShapeRandom(layerObj *layer, shapeObj *shape, long *record)
{
msSetError(MS_QUERYERR, "msPOSTGISLayerGetShapeRandom called but unimplemented!(mapserver not compiled with postgis support)",
"msPOSTGISLayerGetShapeRandom()");
return(MS_FAILURE);
}
int msPOSTGISLayerGetItems(layerObj *layer)
{
msSetError(MS_QUERYERR, "msPOSTGISLayerGetItems called but unimplemented!(mapserver not compiled with postgis support)",
"msPOSTGISLayerGetItems()");
return(MS_FAILURE);
}
// end above's #ifdef USE_POSTGIS
#endif
--------------030608090208000001010700--
More information about the MapServer-users
mailing list