mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-26 00:02:08 -05:00
Brendan Morley's postgis threaded extents patch
git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@2935 c8812cc2-4d05-0410-92ff-de0c093fc19c
This commit is contained in:
parent
59bffa930a
commit
ffbf03c76f
@ -1,17 +1,39 @@
|
||||
INCLUDES = -I../../src
|
||||
|
||||
%.moc.cpp: %.h
|
||||
$(MOC) -o $@ $<
|
||||
|
||||
plugindir = ${pkglibdir}
|
||||
plugin_LTLIBRARIES = postgresprovider.la
|
||||
|
||||
|
||||
postgresprovider_la_SOURCES = qgspostgresprovider.cpp \
|
||||
qgspostgresprovider.h \
|
||||
../../src/qgsfeature.cpp \
|
||||
../../src/qgsfeatureattribute.cpp \
|
||||
../../src/qgsrect.cpp \
|
||||
../../src/qgspoint.cpp \
|
||||
../../src/qgsfield.cpp
|
||||
## non-uic files generated from MOC
|
||||
|
||||
postgresprovider_MOC = qgspostgresprovider.moc.cpp
|
||||
|
||||
|
||||
postgresprovider_la_SOURCES = \
|
||||
qgspostgresprovider.cpp \
|
||||
qgspostgresprovider.moc.cpp \
|
||||
qgspostgresprovider.h \
|
||||
../../src/qgsfeature.cpp \
|
||||
../../src/qgsfeatureattribute.cpp \
|
||||
../../src/qgsrect.cpp \
|
||||
../../src/qgspoint.cpp \
|
||||
../../src/qgsfield.cpp \
|
||||
qgspostgisbox2d.cpp \
|
||||
qgspostgisbox2d.h \
|
||||
qgspostgisbox3d.cpp \
|
||||
qgspostgisbox3d.h \
|
||||
qgspostgrescountthread.cpp \
|
||||
qgspostgrescountthread.h \
|
||||
qgspostgresextentthread.cpp \
|
||||
qgspostgresextentthread.h
|
||||
|
||||
BUILT_SOURCES = $(postgresprovider_MOC)
|
||||
|
||||
postgresprovider_la_LIBADD = $(QT_LDADD) $(PG_LIB) $(GEOS_LDADD)
|
||||
postgresprovider_la_LDFLAGS = -avoid-version -module
|
||||
postgresprovider_la_CXXFLAGS = $(EXTRA_CXXFLAGS) $(QT_CXXFLAGS) -I$(PG_INC) $(DEBUG_QGIS) $(GEOS_CFLAGS)
|
||||
|
||||
CLEANFILES = $(BUILT_SOURCES)
|
||||
|
@ -39,7 +39,16 @@
|
||||
#include "../../src/qgsfield.h"
|
||||
#include "../../src/qgsrect.h"
|
||||
|
||||
#include "../../src/qgsprovidercountcalcevent.h"
|
||||
#include "../../src/qgsproviderextentcalcevent.h"
|
||||
|
||||
#include "qgspostgresprovider.h"
|
||||
|
||||
#include "qgspostgrescountthread.h"
|
||||
#include "qgspostgresextentthread.h"
|
||||
|
||||
#include "qgspostgisbox3d.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define QGISEXTERN extern "C" __declspec( dllexport )
|
||||
#else
|
||||
@ -230,6 +239,29 @@ QgsPostgresProvider::QgsPostgresProvider(QString uri):dataSourceUri(uri)
|
||||
getPrimaryKey();
|
||||
selectSQL += " from " + tableName;
|
||||
//--std::cout << "selectSQL: " << (const char *)selectSQL << std::endl;
|
||||
|
||||
// Kick off the long running threads
|
||||
|
||||
std::cout << "QgsPostgresProvider: About to touch mExtentThread" << std::endl;
|
||||
mExtentThread.setConnInfo( connInfo );
|
||||
mExtentThread.setTableName( tableName );
|
||||
mExtentThread.setSqlWhereClause( sqlWhereClause );
|
||||
mExtentThread.setGeometryColumn( geometryColumn );
|
||||
mExtentThread.setCallback( this );
|
||||
std::cout << "QgsPostgresProvider: About to start mExtentThread" << std::endl;
|
||||
mExtentThread.start();
|
||||
std::cout << "QgsPostgresProvider: Main thread just dispatched mExtentThread" << std::endl;
|
||||
|
||||
std::cout << "QgsPostgresProvider: About to touch mCountThread" << std::endl;
|
||||
mCountThread.setConnInfo( connInfo );
|
||||
mCountThread.setTableName( tableName );
|
||||
mCountThread.setSqlWhereClause( sqlWhereClause );
|
||||
mCountThread.setGeometryColumn( geometryColumn );
|
||||
mCountThread.setCallback( this );
|
||||
std::cout << "QgsPostgresProvider: About to start mCountThread" << std::endl;
|
||||
mCountThread.start();
|
||||
std::cout << "QgsPostgresProvider: Main thread just dispatched mCountThread" << std::endl;
|
||||
|
||||
|
||||
} else {
|
||||
// the table is not a geometry table
|
||||
@ -270,7 +302,27 @@ QgsPostgresProvider::QgsPostgresProvider(QString uri):dataSourceUri(uri)
|
||||
|
||||
QgsPostgresProvider::~QgsPostgresProvider()
|
||||
{
|
||||
std::cout << "QgsPostgresProvider: About to wait for mExtentThread" << std::endl;
|
||||
|
||||
mExtentThread.wait();
|
||||
|
||||
std::cout << "QgsPostgresProvider: Finished waiting for mExtentThread" << std::endl;
|
||||
|
||||
std::cout << "QgsPostgresProvider: About to wait for mCountThread" << std::endl;
|
||||
|
||||
mCountThread.wait();
|
||||
|
||||
std::cout << "QgsPostgresProvider: Finished waiting for mCountThread" << std::endl;
|
||||
|
||||
// Make sure all events from threads have been processed
|
||||
// (otherwise they will get destroyed prematurely)
|
||||
QApplication::sendPostedEvents(this, QGis::ProviderExtentCalcEvent);
|
||||
QApplication::sendPostedEvents(this, QGis::ProviderCountCalcEvent);
|
||||
|
||||
PQfinish(connection);
|
||||
|
||||
std::cout << "QgsPostgresProvider: deconstructing." << std::endl;
|
||||
|
||||
//pLog.flush();
|
||||
}
|
||||
|
||||
@ -433,7 +485,7 @@ QgsFeature* QgsPostgresProvider::getNextFeature(std::list<int> const & attlist)
|
||||
{
|
||||
// XXX I'm assuming swapping from big-endian, or network, byte order to little endian
|
||||
#ifdef QGISDEBUG
|
||||
qWarning("swapping endian for oid");
|
||||
//XXX TOO MUCH OUTPUT!!! qWarning("swapping endian for oid");
|
||||
#endif
|
||||
// convert oid to opposite endian
|
||||
// XXX "Opposite?" Umm, that's not enough information.
|
||||
@ -499,8 +551,21 @@ void QgsPostgresProvider::select(QgsRect * rect, bool useIntersect)
|
||||
std::cout << "Binary cursor: " << declare << std::endl;
|
||||
#endif
|
||||
if(useIntersect){
|
||||
declare += " where intersects(" + geometryColumn;
|
||||
declare += ", setsrid('BOX3D(" + rect->stringRep();
|
||||
// declare += " where intersects(" + geometryColumn;
|
||||
// declare += ", GeometryFromText('BOX3D(" + rect->stringRep();
|
||||
// declare += ")'::box3d,";
|
||||
// declare += srid;
|
||||
// declare += "))";
|
||||
|
||||
// Contributed by #qgis irc "creeping"
|
||||
// This version actually invokes PostGIS's use of spatial indexes
|
||||
declare += " where " + geometryColumn;
|
||||
declare += " && GeometryFromText('BOX3D(" + rect->stringRep();
|
||||
declare += ")'::box3d,";
|
||||
declare += srid;
|
||||
declare += ")";
|
||||
declare += " and intersects(" + geometryColumn;
|
||||
declare += ", GeometryFromText('BOX3D(" + rect->stringRep();
|
||||
declare += ")'::box3d,";
|
||||
declare += srid;
|
||||
declare += "))";
|
||||
@ -576,6 +641,13 @@ return gPtr;
|
||||
|
||||
} */
|
||||
|
||||
void QgsPostgresProvider::setExtent( QgsRect* newExtent )
|
||||
{
|
||||
layerExtent.setXmax( newExtent->xMax() );
|
||||
layerExtent.setXmin( newExtent->xMin() );
|
||||
layerExtent.setYmax( newExtent->yMax() );
|
||||
layerExtent.setYmin( newExtent->yMin() );
|
||||
}
|
||||
|
||||
// TODO - make this function return the real extent_
|
||||
QgsRect *QgsPostgresProvider::extent()
|
||||
@ -1190,35 +1262,62 @@ void QgsPostgresProvider::setSubsetString(QString theSQL)
|
||||
calculateExtents();
|
||||
|
||||
}
|
||||
|
||||
long QgsPostgresProvider::getFeatureCount()
|
||||
{
|
||||
// get total number of features
|
||||
QString sql = "select count(*) from " + tableName;
|
||||
if(sqlWhereClause.length() > 0)
|
||||
{
|
||||
sql += " where " + sqlWhereClause;
|
||||
}
|
||||
// get total number of features
|
||||
|
||||
// First get an approximate count; then delegate to
|
||||
// a thread the task of getting the full count.
|
||||
|
||||
QString sql = "select reltuples from pg_catalog.pg_class where relname = '" +
|
||||
tableName + "'";
|
||||
|
||||
std::cerr << "QgsPostgresProvider: Running SQL: " <<
|
||||
sql << std::endl;
|
||||
|
||||
//QString sql = "select count(*) from " + tableName;
|
||||
|
||||
//if(sqlWhereClause.length() > 0)
|
||||
//{
|
||||
// sql += " where " + sqlWhereClause;
|
||||
//}
|
||||
|
||||
PGresult *result = PQexec(connection, (const char *) sql);
|
||||
|
||||
#ifdef QGISDEBUG
|
||||
std::cerr << "QgsPostgresProvider: Approximate Number of features as text: " <<
|
||||
PQgetvalue(result, 0, 0) << std::endl;
|
||||
#endif
|
||||
|
||||
numberFeatures = QString(PQgetvalue(result, 0, 0)).toLong();
|
||||
PQclear(result);
|
||||
|
||||
#ifdef QGISDEBUG
|
||||
std::cerr << "Number of features: " << numberFeatures << std::endl;
|
||||
std::cerr << "QgsPostgresProvider: Approximate Number of features: " <<
|
||||
numberFeatures << std::endl;
|
||||
#endif
|
||||
|
||||
return numberFeatures;
|
||||
}
|
||||
|
||||
// TODO: use the estimateExtents procedure of PostGIS and PostgreSQL 8
|
||||
// This tip thanks to #qgis irc nick "creeping"
|
||||
void QgsPostgresProvider::calculateExtents()
|
||||
{
|
||||
// get the extents
|
||||
// get the approximate extent by retreiving the bounding box
|
||||
// of the first few items with a geometry
|
||||
|
||||
QString sql = "select extent(" + geometryColumn + ") from " + tableName;
|
||||
QString sql = "select box3d(" + geometryColumn + ") from " + tableName +
|
||||
" where ";
|
||||
|
||||
if(sqlWhereClause.length() > 0)
|
||||
{
|
||||
sql += " where " + sqlWhereClause;
|
||||
sql += "(" + sqlWhereClause + ") and ";
|
||||
}
|
||||
|
||||
sql += "not IsEmpty(" + geometryColumn + ") limit 5";
|
||||
|
||||
#if WASTE_TIME
|
||||
sql = "select xmax(extent(" + geometryColumn + ")) as xmax,"
|
||||
"xmin(extent(" + geometryColumn + ")) as xmin,"
|
||||
@ -1226,50 +1325,108 @@ void QgsPostgresProvider::calculateExtents()
|
||||
#endif
|
||||
|
||||
#ifdef QGISDEBUG
|
||||
qDebug("+++++++++QgsPostgresProvider::calculateExtents - Getting extents using schema.table: " + sql);
|
||||
qDebug("QgsPostgresProvider::calculateExtents - Getting approximate extent using: '" + sql + "'");
|
||||
#endif
|
||||
PGresult *result = PQexec(connection, (const char *) sql);
|
||||
std::string box3d = PQgetvalue(result, 0, 0);
|
||||
|
||||
if (box3d != "")
|
||||
|
||||
// TODO: Guard against the result having no rows
|
||||
|
||||
for (int i = 0; i < PQntuples(result); i++)
|
||||
{
|
||||
std::string s;
|
||||
|
||||
box3d = box3d.substr(box3d.find_first_of("(")+1);
|
||||
box3d = box3d.substr(box3d.find_first_not_of(" "));
|
||||
s = box3d.substr(0, box3d.find_first_of(" "));
|
||||
double minx = strtod(s.c_str(), NULL);
|
||||
|
||||
box3d = box3d.substr(box3d.find_first_of(" ")+1);
|
||||
s = box3d.substr(0, box3d.find_first_of(" "));
|
||||
double miny = strtod(s.c_str(), NULL);
|
||||
|
||||
box3d = box3d.substr(box3d.find_first_of(",")+1);
|
||||
box3d = box3d.substr(box3d.find_first_not_of(" "));
|
||||
s = box3d.substr(0, box3d.find_first_of(" "));
|
||||
double maxx = strtod(s.c_str(), NULL);
|
||||
|
||||
box3d = box3d.substr(box3d.find_first_of(" ")+1);
|
||||
s = box3d.substr(0, box3d.find_first_of(" "));
|
||||
double maxy = strtod(s.c_str(), NULL);
|
||||
|
||||
layerExtent.setXmax(maxx);
|
||||
layerExtent.setXmin(minx);
|
||||
layerExtent.setYmax(maxy);
|
||||
layerExtent.setYmin(miny);
|
||||
#ifdef QGISDEBUG
|
||||
QString xMsg;
|
||||
QTextOStream(&xMsg).precision(18);
|
||||
QTextOStream(&xMsg).width(18);
|
||||
QTextOStream(&xMsg) << "Set extents to: " << layerExtent.
|
||||
xMin() << ", " << layerExtent.yMin() << " " << layerExtent.xMax() << ", " << layerExtent.yMax();
|
||||
std::cerr << xMsg << std::endl;
|
||||
#endif
|
||||
// clear query result
|
||||
PQclear(result);
|
||||
std::string box3d = PQgetvalue(result, i, 0);
|
||||
|
||||
if (0 == i)
|
||||
{
|
||||
// create the initial extent
|
||||
layerExtent = QgsPostGisBox3d(box3d);
|
||||
}
|
||||
else
|
||||
{
|
||||
// extend the initial extent
|
||||
QgsPostGisBox3d b = QgsPostGisBox3d(box3d);
|
||||
|
||||
layerExtent.combineExtentWith( &b );
|
||||
}
|
||||
|
||||
std::cout << "QgsPostgresProvider: After row " << i << ", extent is: "
|
||||
<< layerExtent.xMin() << ", " << layerExtent.yMin() <<
|
||||
" " << layerExtent.xMax() << ", " << layerExtent.yMax() << std::endl;
|
||||
|
||||
}
|
||||
|
||||
#ifdef QGISDEBUG
|
||||
QString xMsg;
|
||||
QTextOStream(&xMsg).precision(18);
|
||||
QTextOStream(&xMsg).width(18);
|
||||
QTextOStream(&xMsg) << "QgsPostgresProvider: Set extents to: " << layerExtent.
|
||||
xMin() << ", " << layerExtent.yMin() << " " << layerExtent.xMax() << ", " << layerExtent.yMax();
|
||||
std::cerr << xMsg << std::endl;
|
||||
#endif
|
||||
|
||||
std::cout << "QgsPostgresProvider: Set limit 5 extents to: "
|
||||
<< layerExtent.xMin() << ", " << layerExtent.yMin() <<
|
||||
" " << layerExtent.xMax() << ", " << layerExtent.yMax() << std::endl;
|
||||
|
||||
// clear query result
|
||||
PQclear(result);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Event sink for events from threads
|
||||
*/
|
||||
void QgsPostgresProvider::customEvent( QCustomEvent * e )
|
||||
{
|
||||
std::cout << "QgsPostgresProvider: received a custom event " << e->type() << std::endl;
|
||||
|
||||
switch ( e->type() )
|
||||
{
|
||||
case (QEvent::Type) QGis::ProviderExtentCalcEvent:
|
||||
|
||||
std::cout << "QgsPostgresProvider: extent has been calculated" << std::endl;
|
||||
|
||||
// Collect the new extent from the event and set this layer's
|
||||
// extent with it.
|
||||
|
||||
setExtent( (QgsRect*) e->data() );
|
||||
|
||||
|
||||
std::cout << "QgsPostgresProvider: new extent has been saved" << std::endl;
|
||||
|
||||
std::cout << "QgsPostgresProvider: Set extent to: "
|
||||
<< layerExtent.xMin() << ", " << layerExtent.yMin() <<
|
||||
" " << layerExtent.xMax() << ", " << layerExtent.yMax() << std::endl;
|
||||
|
||||
std::cout << "QgsPostgresProvider: emitting fullExtentCalculated()" << std::endl;
|
||||
|
||||
emit fullExtentCalculated();
|
||||
|
||||
// TODO: Only uncomment this when the overview map canvas has been subclassed
|
||||
// from the QgsMapCanvas
|
||||
|
||||
// std::cout << "QgsPostgresProvider: emitting repaintRequested()" << std::endl;
|
||||
// emit repaintRequested();
|
||||
|
||||
break;
|
||||
|
||||
case (QEvent::Type) QGis::ProviderCountCalcEvent:
|
||||
|
||||
std::cout << "QgsPostgresProvider: count has been calculated" << std::endl;
|
||||
|
||||
QgsProviderCountCalcEvent* e1 = (QgsProviderCountCalcEvent*) e;
|
||||
|
||||
long numberFeatures = e1->numberFeatures();
|
||||
|
||||
std::cout << "QgsPostgresProvider: count is " << numberFeatures << std::endl;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
std::cout << "QgsPostgresProvider: Finished processing custom event " << e->type() << std::endl;
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool QgsPostgresProvider::deduceEndian()
|
||||
{
|
||||
// need to store the PostgreSQL endian format used in binary cursors
|
||||
@ -1391,7 +1548,9 @@ bool QgsPostgresProvider::getGeometryDetails()
|
||||
#endif
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Class factory to return a pointer to a newly created
|
||||
* QgsPostgresProvider object
|
||||
|
@ -1,5 +1,5 @@
|
||||
/***************************************************************************
|
||||
qgspostgresprovider.h - Data provider for PostgrSQL/PostGIS layers
|
||||
qgspostgresprovider.h - Data provider for PostgreSQL/PostGIS layers
|
||||
-------------------
|
||||
begin : Jan 2, 2004
|
||||
copyright : (C) 2003 by Gary E.Sherman
|
||||
@ -15,13 +15,21 @@
|
||||
* *
|
||||
***************************************************************************/
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef QGSPOSTGRESPROVIDER_H
|
||||
#define QGSPOSTGRESPROVIDER_H
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <libpq-fe.h>
|
||||
}
|
||||
#include "../../src/qgsvectordataprovider.h"
|
||||
#include "../../src/qgsrect.h"
|
||||
#include <list>
|
||||
#include<qstring.h>
|
||||
#include <fstream>
|
||||
#include <qstring.h>
|
||||
#include <qobject.h>
|
||||
//#include <qmutex.h>
|
||||
|
||||
class QgsFeature;
|
||||
class QgsField;
|
||||
@ -29,9 +37,12 @@ class OGRDataSource;
|
||||
class OGRLayer;
|
||||
#include "qgsdatasourceuri.h"
|
||||
|
||||
#include "qgspostgrescountthread.h"
|
||||
#include "qgspostgresextentthread.h"
|
||||
|
||||
/**
|
||||
\class QgsPostgresProvider
|
||||
\brief Data provider for PostgrSQL/PostGIS layers.
|
||||
\brief Data provider for PostgreSQL/PostGIS layers.
|
||||
|
||||
This provider implements the
|
||||
interface defined in the QgsDataProvider class to provide access to spatial
|
||||
@ -39,6 +50,9 @@ data residing in a PostgreSQL/PostGIS enabled database.
|
||||
*/
|
||||
class QgsPostgresProvider:public QgsVectorDataProvider
|
||||
{
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor for the provider. The uri must be in the following format:
|
||||
@ -142,6 +156,12 @@ public:
|
||||
*/
|
||||
QString endianString();
|
||||
|
||||
/**
|
||||
* Changes the stored extent for this layer to the supplied extent.
|
||||
* For example, this is called when the extent worker thread has a result.
|
||||
*/
|
||||
void setExtent( QgsRect* newExtent );
|
||||
|
||||
/** Return the extent for this data layer
|
||||
*/
|
||||
virtual QgsRect *extent();
|
||||
@ -237,6 +257,26 @@ public:
|
||||
|
||||
/**Returns a bitmask containing the supported capabilities*/
|
||||
int capabilities() const;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* This is emitted whenever the worker thread has fully calculated the
|
||||
* PostGIS extents for this layer, and its event has been received by this
|
||||
* provider.
|
||||
*/
|
||||
void fullExtentCalculated();
|
||||
|
||||
/**
|
||||
* This is emitted when this provider is satisfied that all objects
|
||||
* have had a chance to adjust themselves after they'd been notified that
|
||||
* the full extent is available.
|
||||
*
|
||||
* \note It currently isn't being emitted because we don't have an easy way
|
||||
* for the overview canvas to only be repainted. In the meantime
|
||||
* we are satisfied for the overview to reflect the new extent
|
||||
* when the user adjusts the extent of the main map canvas.
|
||||
*/
|
||||
void repaintRequested();
|
||||
|
||||
private:
|
||||
|
||||
@ -250,6 +290,14 @@ private:
|
||||
//! Data source URI struct for this layer
|
||||
QgsDataSourceURI mUri;
|
||||
|
||||
|
||||
//! Child thread for calculating extents.
|
||||
QgsPostgresExtentThread mExtentThread;
|
||||
|
||||
//! Child thread for calculating count.
|
||||
QgsPostgresCountThread mCountThread;
|
||||
|
||||
|
||||
/**
|
||||
* Pointer to the PostgreSQL query result object. If this pointer is 0,
|
||||
* there is no current selection set. Any future getNextFeature requests
|
||||
@ -361,4 +409,11 @@ private:
|
||||
//! Calculate the extents of the layer
|
||||
void calculateExtents();
|
||||
|
||||
/**
|
||||
* Event sink for events from threads
|
||||
*/
|
||||
void customEvent ( QCustomEvent * e );
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user