From: Toby C. <th...@us...> - 2007-08-20 12:42:51
|
Update of /cvsroot/playerstage/code/player/server/drivers/vectormap In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv20455/server/drivers/vectormap Added Files: .cvsignore Makefile.am dbconn.cc dbconn.h postgis.cc Log Message: added vectormap interface added postgis vectormap driver Thanks to Ben Morelli for these changes --- NEW FILE: .cvsignore --- Makefile Makefile.in --- NEW FILE: dbconn.cc --- #include <stdio.h> #include <stdlib.h> #include <iostream> #include "dbconn.h" #ifdef HAVE_GEOS #include <geos_c.h> #endif using namespace std; bool PostgresConn::Connect(const char* dbname, const char* host, const char* user, const char* password, const char* port) { conn = PQsetdbLogin(host, port, NULL, NULL, dbname, user, password); return (PQstatus(conn) != CONNECTION_BAD); } bool PostgresConn::Disconnect() { PQfinish(conn); conn = NULL; //return (PQstatus(conn) == CONNECTION_BAD); return true; } VectorMapInfoHolder PostgresConn::GetVectorMapInfo(vector<string> layerNames) { // Get the extent in the first query string query_string = "SELECT asbinary(extent(geom)) FROM (SELECT geom FROM "; for (uint ii=0; ii<layerNames.size(); ++ii) { if (ii == 0) { query_string += layerNames[ii]; } else { query_string += " UNION SELECT geom FROM " + string(layerNames[ii]); } } query_string += ") AS layer_extent;"; //PGresult* res = PQexec(conn, query_string.c_str()); PGresult* res = PQexecParams(conn, query_string.c_str(), 0, NULL, NULL, NULL, NULL, 1); if (PQresultStatus(res) != PGRES_TUPLES_OK) { cout << "Error performing select query on database!" << endl; cout << "No extent value found." << endl; cout << "GetVectorMapInfo() failed" << endl; } uint8_t* wkb_temp = reinterpret_cast<uint8_t*>(PQgetvalue(res, 0, 0)); uint length = PQgetlength(res, 0, 0); uint8_t* wkb = new uint8_t[length]; memcpy(wkb, wkb_temp, length); BoundingBox extent = BinaryToBBox(wkb, length); delete[] wkb; // Get the srid in the second query res = PQexec(conn, "SELECT srid FROM geometry_columns LIMIT 1;"); if (PQresultStatus(res) != PGRES_TUPLES_OK) { cout << "Error performing select query on database!" << endl; cout << "No srid value found." << endl; cout << "GetVectorMapInfo() failed" << endl; } uint32_t srid = atoi(PQgetvalue(res, 0, 0)); PQclear(res); VectorMapInfoHolder info(srid, extent); for (uint i=0; i<layerNames.size(); ++i) { LayerInfoHolder layer_info(layerNames[i]); LayerDataHolder layer_data(layer_info); info.layers.push_back(layer_data); } return info; } LayerInfoHolder PostgresConn::GetLayerInfo(const char* layer_name) { LayerInfoHolder info; // Retrieve the extent of the layer in binary form const char* query_template = "SELECT asbinary(extent(geom)) AS extent FROM %s;"; char query_string[MAX_PSQL_STRING]; memset(query_string, 0, MAX_PSQL_STRING); snprintf(query_string, MAX_PSQL_STRING, query_template, layer_name); //PGresult* res = PQexec(conn, query_string); PGresult* res = PQexecParams(conn, query_string, 0, NULL, NULL, NULL, NULL, 1); if (PQresultStatus(res) != PGRES_TUPLES_OK) { cout << "Error performing select query on database!" << endl; cout << "GetLayerInfo() failed" << endl; } info.name = layer_name; uint length = PQgetlength(res, 0, 0); uint8_t* wkb = new uint8_t[length]; memcpy(wkb, PQgetvalue(res, 0, 0), length); info.extent = BinaryToBBox(wkb, length); PQclear(res); return info; } LayerDataHolder PostgresConn::GetLayerData(const char* layer_name) { // Split into two queries. First get the layer meta-data, then get the feature data. // need to get layer name count, layer name, feature count and exteny LayerDataHolder data; data.info.name = layer_name; // need to get name count, name, wkb count, wkb const char* template_data = "SELECT name, asbinary(geom) FROM %s;"; char query_data[MAX_PSQL_STRING]; memset(query_data, 0, sizeof(MAX_PSQL_STRING)); snprintf(query_data, MAX_PSQL_STRING, template_data, layer_name); //res = PQexec(conn, query_data); PGresult* res = PQexecParams(conn, query_data, 0, NULL, NULL, NULL, NULL, 1); if (PQresultStatus(res) != PGRES_TUPLES_OK) { cout << "Error performing select query on database!" << endl; cout << "GetLayerData() data failed, returned NULL" << endl; } int num_rows = PQntuples(res); for (int i=0; i<num_rows; ++i) { FeatureDataHolder fd(string(PQgetvalue(res, i, 0))); uint8_t *wkb = reinterpret_cast<uint8_t *>(PQgetvalue(res, i, 1)); uint32_t length = PQgetlength(res, i, 1); fd.wkb.assign(wkb, &wkb[length]); data.features.push_back(fd); } PQclear(res); return data; } BoundingBox PostgresConn::BinaryToBBox(const uint8_t* wkb, uint length) { BoundingBox res; memset(&res, 0, sizeof(BoundingBox)); #ifdef HAVE_GEOS GEOSGeom polygon = NULL; polygon = GEOSGeomFromWKB_buf(wkb, length); if (polygon == NULL) { printf("GEOSGeomFromWKB_buf returned NULL!\n"); return res; } GEOSGeom linestring = GEOSGetExteriorRing(polygon); if (linestring == NULL) { printf("GEOSGetExteriorRing returned NULL!\n"); return res; } GEOSCoordSeq coords = GEOSGeom_getCoordSeq(linestring); if (coords == NULL) { printf("GEOSGeom_getCoordSeq returned NULL!\n"); return res; } double xmin = INT_MAX, ymin = INT_MAX; double xmax = INT_MIN, ymax = INT_MIN; double tempX, tempY = 0; for (int ii=0; ii<GEOSGetNumCoordinates(linestring); ++ii) { GEOSCoordSeq_getX(coords, ii, &tempX); GEOSCoordSeq_getY(coords, ii, &tempY); if (tempX > xmax) xmax = tempX; if (tempX < xmin) xmin = tempX; if (tempY > ymax) ymax = tempY; if (tempY < ymin) ymin = tempY; } res.x0 = xmin; res.y0 = ymin; res.x1 = xmax; res.y1 = ymax; #endif return res; } const player_vectormap_info_t* VectorMapInfoHolder::Convert() { info.srid = srid; info.extent.x0 = extent.x0; info.extent.y0 = extent.y0; info.extent.x1 = extent.x1; info.extent.y1 = extent.y1; info.layers_count = layers.size(); info.layers = new player_vectormap_layer_data_t[layers.size()]; for (uint ii=0; ii<layers.size(); ++ii) { info.layers[ii] = *(layers[ii].Convert()); } return &info; } VectorMapInfoHolder::~VectorMapInfoHolder() { // if (info.layers) // { // delete[] info.layers; // } } const player_vectormap_layer_info_t* LayerInfoHolder::Convert() { layer_info.name = strdup(name.c_str()); layer_info.name_count = name.size() + 1; layer_info.extent.x0 = extent.x0; layer_info.extent.y0 = extent.y0; layer_info.extent.x1 = extent.x1; layer_info.extent.y1 = extent.y1; return &layer_info; } const player_vectormap_feature_data_t* FeatureDataHolder::Convert() { feature_data.name = strdup(name.c_str()); feature_data.name_count = name.size() + 1; feature_data.wkb = new uint8_t[wkb.size()]; feature_data.wkb_count = wkb.size(); ///TODO: Make more efficient for (uint ii=0; ii<wkb.size(); ++ii) { feature_data.wkb[ii] = wkb[ii]; } return &feature_data; } FeatureDataHolder::~FeatureDataHolder() { // if (feature_data.wkb) // { // delete[] feature_data.wkb; // } } const player_vectormap_layer_data_t* LayerDataHolder::Convert() { layer_data.info = *(info.Convert()); layer_data.features_count = features.size(); layer_data.features = new player_vectormap_feature_data_t[features.size()]; for (uint ii=0; ii<features.size(); ++ii) { layer_data.features[ii] = *(features[ii].Convert()); } return &layer_data; } LayerDataHolder::~LayerDataHolder() { // if (layer_data.features) // { // delete[] layer_data.features; // } } --- NEW FILE: dbconn.h --- #ifndef __DBCONN_H_ #define __DBCONN_H_ #include <postgresql/libpq-fe.h> #include <libplayercore/playercore.h> #include <libplayercore/error.h> #include <vector> #define MAX_PSQL_STRING 256 using namespace std; typedef struct { double x0, y0, x1, y1; }BoundingBox; class FeatureDataHolder { public: FeatureDataHolder(){}; ~FeatureDataHolder(); FeatureDataHolder(string name) { this->name = name; }; const player_vectormap_feature_data_t* Convert(); string name; vector<uint8_t> wkb; player_vectormap_feature_data_t feature_data; }; class LayerInfoHolder { public: LayerInfoHolder(){}; LayerInfoHolder(string name) { this->name = name; memset(&extent, 0, sizeof(extent)); }; const player_vectormap_layer_info_t* Convert(); string name; BoundingBox extent; player_vectormap_layer_info_t layer_info; }; class LayerDataHolder { public: LayerDataHolder(){}; ~LayerDataHolder(); LayerDataHolder(LayerInfoHolder info) { this->info = info; }; const player_vectormap_layer_data_t* Convert(); LayerInfoHolder info; vector<FeatureDataHolder> features; player_vectormap_layer_data_t layer_data; }; class VectorMapInfoHolder { public: VectorMapInfoHolder(){}; ~VectorMapInfoHolder(); VectorMapInfoHolder(uint32_t srid, BoundingBox extent) { this->srid = srid; this->extent = extent; }; const player_vectormap_info_t* Convert(); uint32_t srid; vector<LayerDataHolder> layers; BoundingBox extent; player_vectormap_info_t info; }; class PostgresConn { public: PostgresConn(){ conn = NULL; }; ~PostgresConn(){ if (Connected()) Disconnect(); }; bool Connect(const char* dbname, const char* host, const char* user, const char* password, const char* port); bool Disconnect(); bool Connected() { return (conn != NULL) && (PQstatus(conn) != CONNECTION_BAD); }; const char* ErrorMessage() { return strdup(PQerrorMessage(conn)); }; VectorMapInfoHolder GetVectorMapInfo(vector<string> layerNames); LayerInfoHolder GetLayerInfo(const char *layer_name); LayerDataHolder GetLayerData(const char *layer_name); private: BoundingBox BinaryToBBox(const uint8_t *binary, uint length); PGconn *conn; }; #endif /* __DBCONN_H_ */ --- NEW FILE: Makefile.am --- noinst_LTLIBRARIES = if INCLUDE_POSTGIS noinst_LTLIBRARIES += libpostgis.la endif AM_CPPFLAGS = -Wall -I$(top_srcdir) $(LIBPQXX_CFLAGS) libpostgis_la_SOURCES = postgis.cc dbconn.cc dbconn.h libpostgis_la_LIBADD = @GEOS_LIBS@ --- NEW FILE: postgis.cc --- /* * Player - One Hell of a Robot Server * Copyright (C) 2004 Brian Gerkey ge...@st... * * This program 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. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* * $Id: postgis.cc,v 1.1 2007/08/20 19:42:48 thjc Exp $ * * A driver to use a vector map with a PostGIS backend. */ /** @ingroup drivers */ /** @{ */ /** @defgroup driver_PostGIS PostGIS * @brief Vectormap driver based on a Postgresql database with the PostGIS plugin. @par Provides - @ref interface_vectormap @par Requires - None @par Configuration requests - None @par Configuration file options - dbname (string) - Default: template1 - The name of the Postgresql database to connect to. - host (string) - Default: localhost - The name of the database host. - user (string) - Default: postgres - The name of the database user. - port (string) - Default: 5432 - The port of the database. - password (string) - Default: empty string - The password for the database user - layers (string tuple) - Default: Field required - Names of the layers. The layers are named after the corresponding tables in the database. @par Example @verbatim driver ( dbname "gis" host "192.168.0.2" port "5433" user "postgres" password "secret" layers ["obstacles_geom" "markers_geom"] ) @endverbatim @par Creating a PostGIS Database ///TODO: Add documentation For more information see http://postgis.refractions.net/ @par Database schema ///TODO: Add documentation @author Ben Morelli */ /** @} */ #include <sys/types.h> // required by Darwin #include <stdlib.h> #include <libplayercore/playercore.h> #include <libplayercore/error.h> #include <playererror.h> #include "dbconn.h" #ifdef HAVE_GEOS #include <geos_c.h> #endif /** Dummy function passed as a function pointer GEOS when it is initialised. GEOS uses this for logging. */ void geosprint(const char *text, ...) { return; } //////////////////////////////////////////////////////////////////////////////// class PostGIS : public Driver { public: PostGIS(ConfigFile* cf, int section, const char* dbname, const char* host, const char* user, const char* password, const char* port, vector<string> layerNames); ~PostGIS(); int Setup(); int Shutdown(); // MessageHandler int ProcessMessage(MessageQueue * resp_queue, player_msghdr * hdr, void * data); private: void RequestLayerWrite(player_vectormap_layer_data_t* data); VectorMapInfoHolder RequestVectorMapInfo(); LayerInfoHolder RequestLayerInfo(const char* layer_name); LayerDataHolder RequestLayerData(const char* layer_name); const char* dbname; const char* host; const char* user; const char* password; const char* port; vector<string> layerNames; PostgresConn *conn; }; //////////////////////////////////////////////////////////////////////////////// Driver* PostGIS_Init(ConfigFile* cf, int section) { const char* dbname = cf->ReadString(section,"dbname", "template1"); const char* host = cf->ReadString(section,"host", "localhost"); const char* user = cf->ReadString(section,"user", "postgres"); const char* password = cf->ReadString(section,"password", ""); const char* port = cf->ReadString(section,"port", "5432"); vector<string> layerNames; int layers_count = cf->GetTupleCount(section, "layers"); if (layers_count < 1) { PLAYER_ERROR("There must be at least one layer defined in the 'layers' configuration field."); return NULL; } for (int i=0; i<layers_count; ++i) { const char* layer_name = cf->ReadTupleString(section, "layers", i, ""); layerNames.push_back(string(layer_name)); } return((Driver*)(new PostGIS(cf, section, dbname, host, user, password, port, layerNames))); } //////////////////////////////////////////////////////////////////////////////// // a driver registration function void PostGIS_Register(DriverTable* table) { table->AddDriver("postgis", PostGIS_Init); } //////////////////////////////////////////////////////////////////////////////// PostGIS::PostGIS(ConfigFile* cf, int section, const char* dbname, const char* host, const char* user, const char* password, const char* port, vector<string> layerNames) : Driver(cf, section, true, PLAYER_MSGQUEUE_DEFAULT_MAXLEN, PLAYER_VECTORMAP_CODE) { this->dbname = dbname; this->host = host; this->user = user; this->password = password; this->port = port; this->layerNames = layerNames; this->conn = NULL; } //////////////////////////////////////////////////////////////////////////////// PostGIS::~PostGIS() { /*if (conn != NULL && conn->Connected()) { conn->Disconnect(); } if (conn != NULL) { printf("DELETING CONNECTION\n"); delete conn; conn = NULL; }*/ } //////////////////////////////////////////////////////////////////////////////// // Load resources int PostGIS::Setup() { PLAYER_MSG0(2, "PostGIS vectormap initialising"); #ifdef HAVE_GEOS PLAYER_MSG0(2, "Initialising GEOS"); initGEOS(geosprint, geosprint); PLAYER_MSG0(2, "GEOS Initialised"); #endif conn = new PostgresConn(); conn->Connect(dbname, host, user, password, port); if (!conn->Connected()) { PLAYER_ERROR("Could not connect to Postgres database!"); PLAYER_ERROR1("%s",conn->ErrorMessage()); return(1); } PLAYER_MSG0(2, "PostGIS vectormap ready"); return(0); } //////////////////////////////////////////////////////////////////////////////// // Clean up resources int PostGIS::Shutdown() { PLAYER_MSG0(2, "PostGIS vectormap shutting down"); if (conn != NULL && conn->Connected()) { PLAYER_MSG0(2, "Disconnecting database"); conn->Disconnect(); } if (conn != NULL) { delete conn; //conn == NULL; } #ifdef HAVE_GEOS PLAYER_MSG0(2, "Shutting down GEOS"); finishGEOS(); #endif PLAYER_MSG0(2, "PostGIS vectormap stopped"); return(0); } //////////////////////////////////////////////////////////////////////////////// // Process an incoming message int PostGIS::ProcessMessage(MessageQueue * resp_queue, player_msghdr * hdr, void * data) { // Request for map info /////////////////////////////////////////////////////////// if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ, PLAYER_VECTORMAP_REQ_GET_MAP_INFO, this->device_addr)) { if (hdr->size != 0) { PLAYER_ERROR2("request is wrong length (%d != %d); ignoring", hdr->size, 0); return -1; } VectorMapInfoHolder info = RequestVectorMapInfo(); const player_vectormap_info_t* response = info.Convert(); this->Publish(this->device_addr, resp_queue, PLAYER_MSGTYPE_RESP_ACK, PLAYER_VECTORMAP_REQ_GET_MAP_INFO, (void*)response, sizeof(player_vectormap_info_t), NULL); return(0); } // Request for layer info ///////////////////////////////////////////////////////// else if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ, PLAYER_VECTORMAP_REQ_GET_LAYER_INFO, this->device_addr)) { if (hdr->size != sizeof(player_vectormap_layer_info_t)) { PLAYER_ERROR2("request is wrong length (%d != %d); ignoring", hdr->size, 0); return -1; } const player_vectormap_layer_info_t* request = reinterpret_cast<player_vectormap_layer_info_t*>(data); LayerInfoHolder info = RequestLayerInfo(request->name); const player_vectormap_layer_info_t* response = info.Convert(); this->Publish(this->device_addr, resp_queue, PLAYER_MSGTYPE_RESP_ACK, PLAYER_VECTORMAP_REQ_GET_LAYER_INFO, (void*)response, sizeof(player_vectormap_layer_info_t), NULL); return(0); } // Request for layer data ///////////////////////////////////////////////////////// else if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ, PLAYER_VECTORMAP_REQ_GET_LAYER_DATA, this->device_addr)) { if (hdr->size != sizeof(player_vectormap_layer_data_t)) { PLAYER_ERROR2("request is wrong length (%d != %d); ignoring", hdr->size, 0); return -1; } player_vectormap_layer_data_t* request = reinterpret_cast<player_vectormap_layer_data_t*>(data); LayerDataHolder ldata = RequestLayerData(request->info.name); const player_vectormap_layer_data_t* response = ldata.Convert(); this->Publish(this->device_addr, resp_queue, PLAYER_MSGTYPE_RESP_ACK, PLAYER_VECTORMAP_REQ_GET_LAYER_DATA, (void*)response, sizeof(player_vectormap_layer_data_t), NULL); return(0); } // Request to write layer data /////////////////////////////////////////////////// else if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ, PLAYER_VECTORMAP_REQ_WRITE_LAYER, this->device_addr)) { if (hdr->size != 0) { PLAYER_ERROR2("request is wrong length (%d != %d); ignoring", hdr->size, 0); return -1; } player_vectormap_layer_data_t* request = reinterpret_cast<player_vectormap_layer_data_t*>(data); RequestLayerWrite(request); this->Publish(this->device_addr, resp_queue, PLAYER_MSGTYPE_RESP_ACK, PLAYER_VECTORMAP_REQ_WRITE_LAYER, (void*)request, sizeof(player_vectormap_layer_data_t), NULL); return(0); } // Don't know how to handle this message ///////////////////////////////////////// return(-1); } VectorMapInfoHolder PostGIS::RequestVectorMapInfo() { if (conn == NULL || conn->Connected() == false) { PLAYER_ERROR("PostGis::RequestVectorMapInfo() failed! No db connection."); } VectorMapInfoHolder info = conn->GetVectorMapInfo(layerNames); return info; } LayerInfoHolder PostGIS::RequestLayerInfo(const char* layer_name) { if (conn == NULL || conn->Connected() == false) { PLAYER_ERROR("PostGis::RequestLayerInfo() failed! No db connection."); } LayerInfoHolder info = conn->GetLayerInfo(layer_name); return info; } LayerDataHolder PostGIS::RequestLayerData(const char* layer_name) { if (conn == NULL || conn->Connected() == false) { PLAYER_ERROR("PostGis::RequestLayerData() failed! No db connection."); } LayerDataHolder data = conn->GetLayerData(layer_name); return data; } void PostGIS::RequestLayerWrite(player_vectormap_layer_data_t* data) { return; } |