Skip to content

Commit 4512133

Browse files
committedFeb 28, 2013
[FEATURE] new OpenStreetMap data access API and GUI
The idea is to replace the current OSM provider+plugin by this new code. Differences from old code: - read-only access - without editing and upload support - no special provider (using SpatiaLite provider) - underlying OSM topology accessible from API - download using Overpass API: fast, customizable, nearly unlimited download - OSM XML files have to be first imported to a Sqlite3 database, then SpatiaLite layers can be exported
1 parent 94f8f73 commit 4512133

24 files changed

+2821
-1
lines changed
 

‎src/analysis/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ SET(QGIS_ANALYSIS_SRCS
3737
vector/qgsgeometryanalyzer.cpp
3838
vector/qgszonalstatistics.cpp
3939
vector/qgsoverlayanalyzer.cpp
40+
openstreetmap/qgsosmbase.cpp
41+
openstreetmap/qgsosmdatabase.cpp
42+
openstreetmap/qgsosmdownload.cpp
43+
openstreetmap/qgsosmimport.cpp
4044
)
4145

4246
INCLUDE_DIRECTORIES(BEFORE raster)
@@ -64,6 +68,8 @@ IF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
6468
ENDIF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
6569

6670
SET(QGIS_ANALYSIS_MOC_HDRS
71+
openstreetmap/qgsosmdownload.h
72+
openstreetmap/qgsosmimport.h
6773
)
6874

6975
QT4_WRAP_CPP(QGIS_ANALYSIS_MOC_SRCS ${QGIS_ANALYSIS_MOC_HDRS})
@@ -85,6 +91,10 @@ SET(QGIS_ANALYSIS_HDRS
8591
interpolation/qgsgridfilewriter.h
8692
interpolation/qgsidwinterpolator.h
8793
interpolation/qgstininterpolator.h
94+
openstreetmap/qgsosmbase.h
95+
openstreetmap/qgsosmdatabase.h
96+
openstreetmap/qgsosmdownload.h
97+
openstreetmap/qgsosmimport.h
8898
)
8999

90100
INCLUDE_DIRECTORIES(
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include "qgsosmbase.h"
2+
3+
// nothing here now
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#ifndef OSMBASE_H
2+
#define OSMBASE_H
3+
4+
#include <QString>
5+
6+
#include "qgspoint.h"
7+
8+
#include <sqlite3.h>
9+
10+
typedef qint64 QgsOSMId;
11+
12+
class QgsOSMDatabase;
13+
14+
struct QgsOSMElementID
15+
{
16+
enum Type { Invalid, Node, Way, Relation };
17+
18+
Type type;
19+
QgsOSMId id;
20+
};
21+
22+
23+
/**
24+
Elements (also data primitives) are the basic components in OpenStreetMap from which everything else
25+
is defined. These consist of Nodes (which define a point in space), Ways (which define a linear features
26+
and areas), and Relations - with an optional role - which are sometimes used to define the relation
27+
between other elements. All of the above can have one of more associated tags.
28+
*/
29+
class ANALYSIS_EXPORT QgsOSMElement
30+
{
31+
public:
32+
QgsOSMElement() { mElemID.type = QgsOSMElementID::Invalid; mElemID.id = 0; }
33+
QgsOSMElement( QgsOSMElementID::Type t, QgsOSMId id ) { mElemID.type = t; mElemID.id = id; }
34+
35+
bool isValid() const { return mElemID.type != QgsOSMElementID::Invalid; }
36+
37+
QgsOSMDatabase* database() const;
38+
39+
// fetched automatically from DB
40+
QgsOSMElementID elemID() const { return mElemID; }
41+
int id() const { return mElemID.id; }
42+
//QString username() const;
43+
//QDateTime timestamp() const;
44+
//int version() const;
45+
46+
private:
47+
QgsOSMElementID mElemID;
48+
};
49+
50+
51+
52+
/**
53+
A node is one of the core elements in the OpenStreetMap data model. It consists of a single geospatial
54+
point using a latitude and longitude. A third optional dimension, altitude, can be recorded; key:ele
55+
and a node can also be defined at a particular layer=* or level=*. Nodes can be used to define standalone
56+
point features or be used to define the path of a way.
57+
*/
58+
class ANALYSIS_EXPORT QgsOSMNode : public QgsOSMElement
59+
{
60+
public:
61+
QgsOSMNode() : mPoint() {}
62+
QgsOSMNode( QgsOSMId id, const QgsPoint& point ) : QgsOSMElement( QgsOSMElementID::Node, id ), mPoint( point ) {}
63+
64+
QgsPoint point() const { return mPoint; }
65+
66+
// fetched on-demand
67+
QList<QgsOSMElementID> ways() const; // where the node participates?
68+
QList<QgsOSMElementID> relations() const;
69+
70+
private:
71+
QgsPoint mPoint;
72+
};
73+
74+
75+
/**
76+
A way is an ordered list of nodes which normally also has at least one tag or is included within
77+
a Relation. A way can have between 2 and 2,000 nodes, although it's possible that faulty ways with zero
78+
or a single node exist. A way can be open or closed. A closed way is one whose last node on the way
79+
is also the first on that way. A closed way may be interpreted either as a closed polyline, or an area,
80+
or both.
81+
*/
82+
class ANALYSIS_EXPORT QgsOSMWay : public QgsOSMElement
83+
{
84+
public:
85+
QgsOSMWay() {}
86+
QgsOSMWay( QgsOSMId id, const QList<QgsOSMId> nodes ) : QgsOSMElement( QgsOSMElementID::Way, id ), mNodes( nodes ) {}
87+
88+
QList<QgsOSMId> nodes() const { return mNodes; }
89+
90+
// fetched on-demand
91+
//QList<OSMElementID> relations() const;
92+
93+
private:
94+
QList<QgsOSMId> mNodes;
95+
};
96+
97+
98+
#if 0
99+
/**
100+
A relation is one of the core data elements that consists of one or more tags and also an ordered list
101+
of one or more nodes and/or ways as members which is used to define logical or geographic relationships
102+
between other elements. A member of a relation can optionally have a role which describe the part that
103+
a particular feature plays within a relation.
104+
*/
105+
class ANALYSIS_EXPORT QgsOSMRelation : public QgsOSMElement
106+
{
107+
public:
108+
QString relationType() const;
109+
110+
QList< QPair<QgsOSMElementID, QString> > members() const;
111+
};
112+
#endif
113+
114+
115+
class ANALYSIS_EXPORT QgsOSMTags
116+
{
117+
public:
118+
QgsOSMTags() {}
119+
120+
int count() const { return mMap.count(); }
121+
QList<QString> keys() const { return mMap.keys(); }
122+
bool contains( const QString& k ) const { return mMap.contains( k ); }
123+
void insert( const QString& k, const QString& v ) { mMap.insert( k, v ); }
124+
QString value( const QString& k ) const { return mMap.value( k ); }
125+
126+
private:
127+
QMap<QString, QString> mMap;
128+
};
129+
130+
#endif // OSMBASE_H

‎src/analysis/openstreetmap/qgsosmdatabase.cpp

Lines changed: 597 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#ifndef OSMDATABASE_H
2+
#define OSMDATABASE_H
3+
4+
#include <QString>
5+
#include <QStringList>
6+
7+
#include "qgsosmbase.h"
8+
9+
#include "qgsgeometry.h"
10+
11+
class QgsOSMNodeIterator;
12+
class QgsOSMWayIterator;
13+
14+
typedef QPair<QString, int> QgsOSMTagCountPair;
15+
16+
/**
17+
* Class that encapsulates access to OpenStreetMap data stored in a database
18+
* previously imported from XML file.
19+
*/
20+
class ANALYSIS_EXPORT QgsOSMDatabase
21+
{
22+
public:
23+
explicit QgsOSMDatabase( const QString& dbFileName = QString() );
24+
~QgsOSMDatabase();
25+
26+
void setFileName( const QString& dbFileName ) { mDbFileName = dbFileName; }
27+
QString filename() const { return mDbFileName; }
28+
bool isOpen() const;
29+
30+
bool open();
31+
bool close();
32+
33+
QString errorString() const { return mError; }
34+
35+
// data access
36+
37+
int countNodes() const;
38+
int countWays() const;
39+
40+
QgsOSMNodeIterator listNodes() const;
41+
QgsOSMWayIterator listWays() const;
42+
43+
QgsOSMNode node( QgsOSMId id ) const;
44+
QgsOSMWay way( QgsOSMId id ) const;
45+
//OSMRelation relation( OSMId id ) const;
46+
47+
QgsOSMTags tags( bool way, QgsOSMId id ) const;
48+
49+
QList<QgsOSMTagCountPair> usedTags( bool ways ) const;
50+
51+
QgsPolyline wayPoints( QgsOSMId id ) const;
52+
53+
// export to spatialite
54+
55+
enum ExportType { Point, Polyline, Polygon };
56+
bool exportSpatiaLite( ExportType type, const QString& tableName, const QStringList& tagKeys = QStringList() );
57+
58+
protected:
59+
bool prepareStatements();
60+
int runCountStatement( const char* sql ) const;
61+
void deleteStatement( sqlite3_stmt*& stmt );
62+
63+
void exportSpatiaLiteNodes( const QString& tableName, const QStringList& tagKeys );
64+
void exportSpatiaLiteWays( bool closed, const QString& tableName, const QStringList& tagKeys );
65+
bool createSpatialTable( const QString& tableName, const QString& geometryType, const QStringList& tagKeys );
66+
bool createSpatialIndex( const QString& tableName );
67+
68+
QString quotedIdentifier( QString id );
69+
QString quotedValue( QString value );
70+
71+
private:
72+
//! database file name
73+
QString mDbFileName;
74+
75+
QString mError;
76+
77+
//! pointer to sqlite3 database that keeps OSM data
78+
sqlite3* mDatabase;
79+
80+
sqlite3_stmt* mStmtNode;
81+
sqlite3_stmt* mStmtNodeTags;
82+
sqlite3_stmt* mStmtWay;
83+
sqlite3_stmt* mStmtWayNode;
84+
sqlite3_stmt* mStmtWayNodePoints;
85+
sqlite3_stmt* mStmtWayTags;
86+
};
87+
88+
89+
/** Encapsulate iteration over table of nodes */
90+
class ANALYSIS_EXPORT QgsOSMNodeIterator
91+
{
92+
public:
93+
~QgsOSMNodeIterator();
94+
95+
QgsOSMNode next();
96+
void close();
97+
98+
protected:
99+
QgsOSMNodeIterator( sqlite3* handle );
100+
101+
sqlite3_stmt* mStmt;
102+
103+
friend class QgsOSMDatabase;
104+
};
105+
106+
107+
108+
/** Encapsulate iteration over table of ways */
109+
class ANALYSIS_EXPORT QgsOSMWayIterator
110+
{
111+
public:
112+
~QgsOSMWayIterator();
113+
114+
QgsOSMWay next();
115+
void close();
116+
117+
protected:
118+
QgsOSMWayIterator( sqlite3* handle );
119+
120+
sqlite3_stmt* mStmt;
121+
122+
friend class QgsOSMDatabase;
123+
};
124+
125+
126+
127+
#endif // OSMDATABASE_H
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#include "qgsosmdownload.h"
2+
3+
#include <QNetworkAccessManager>
4+
#include <QNetworkRequest>
5+
#include <QNetworkReply>
6+
7+
#include "qgsnetworkaccessmanager.h"
8+
#include "qgsrectangle.h"
9+
10+
11+
QString QgsOSMDownload::defaultServiceUrl()
12+
{
13+
return "http://overpass-api.de/api/interpreter";
14+
}
15+
16+
17+
QString QgsOSMDownload::queryFromRect( const QgsRectangle& rect )
18+
{
19+
return QString( "(node(%1,%2,%3,%4);<;);out;" ).arg( rect.yMinimum() ).arg( rect.xMinimum() )
20+
.arg( rect.yMaximum() ).arg( rect.xMaximum() );
21+
}
22+
23+
24+
QgsOSMDownload::QgsOSMDownload()
25+
: mServiceUrl( defaultServiceUrl() ), mReply( 0 )
26+
{
27+
}
28+
29+
QgsOSMDownload::~QgsOSMDownload()
30+
{
31+
if ( mReply )
32+
{
33+
mReply->abort();
34+
mReply->deleteLater();
35+
mReply = 0;
36+
}
37+
}
38+
39+
40+
bool QgsOSMDownload::start()
41+
{
42+
mError.clear();
43+
44+
if ( mQuery.isEmpty() )
45+
{
46+
mError = tr( "No query has been specified." );
47+
return false;
48+
}
49+
50+
if ( mReply )
51+
{
52+
mError = tr( "There is already a pending request for data." );
53+
return false;
54+
}
55+
56+
if ( !mFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
57+
{
58+
mError = tr( "Cannot open output file: %1" ).arg( mFile.fileName() );
59+
return false;
60+
}
61+
62+
QgsNetworkAccessManager* nwam = QgsNetworkAccessManager::instance();
63+
64+
QUrl url( mServiceUrl );
65+
url.addQueryItem( "data", mQuery );
66+
67+
QNetworkRequest request( url );
68+
request.setRawHeader( "User-Agent", "QGIS" );
69+
70+
mReply = nwam->get( request );
71+
72+
connect( mReply, SIGNAL( readyRead() ), this, SLOT( onReadyRead() ) );
73+
connect( mReply, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( onError( QNetworkReply::NetworkError ) ) );
74+
connect( mReply, SIGNAL( finished() ), this, SLOT( onFinished() ) );
75+
connect( mReply, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SIGNAL( downloadProgress( qint64, qint64 ) ) );
76+
77+
return true;
78+
}
79+
80+
81+
bool QgsOSMDownload::abort()
82+
{
83+
if ( !mReply )
84+
return false;
85+
86+
mReply->abort();
87+
return true;
88+
}
89+
90+
91+
void QgsOSMDownload::onReadyRead()
92+
{
93+
Q_ASSERT( mReply );
94+
95+
QByteArray data = mReply->read( 1024 * 1024 );
96+
mFile.write( data );
97+
}
98+
99+
100+
void QgsOSMDownload::onFinished()
101+
{
102+
qDebug( "finished" );
103+
Q_ASSERT( mReply );
104+
105+
mReply->deleteLater();
106+
mReply = 0;
107+
108+
mFile.close();
109+
110+
emit finished();
111+
}
112+
113+
114+
void QgsOSMDownload::onError( QNetworkReply::NetworkError err )
115+
{
116+
qDebug( "error: %d", err );
117+
Q_ASSERT( mReply );
118+
119+
mError = mReply->errorString();
120+
}
121+
122+
123+
bool QgsOSMDownload::isFinished() const
124+
{
125+
if ( !mReply )
126+
return true;
127+
128+
return mReply->isFinished();
129+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#ifndef OSMDOWNLOAD_H
2+
#define OSMDOWNLOAD_H
3+
4+
5+
#include <QObject>
6+
#include <QFile>
7+
#include <QNetworkReply>
8+
9+
class QgsRectangle;
10+
11+
/**
12+
* @brief OSMDownload is a utility class for downloading OpenStreetMap via Overpass API.
13+
*
14+
* To use this class, it is necessary to set query, output file name and start the request.
15+
* The interface is asynchronous, the caller has to wait for finished() signal that is
16+
* emitted whe the request has finished (successfully or with an error).
17+
*
18+
* To check whether the the request has been successful, check hasError() and use errorString()
19+
* to retreive error message. An error may happen either directly in start() method
20+
* or during the network communication.
21+
*
22+
* By default OSMDownload uses remote service at location returned by defaultServiceUrl() method.
23+
*/
24+
class ANALYSIS_EXPORT QgsOSMDownload : public QObject
25+
{
26+
Q_OBJECT
27+
public:
28+
29+
//! Return URL of the service that is used by default
30+
static QString defaultServiceUrl();
31+
32+
//! Create query (in Overpass Query Language) that fetches everything in given rectangle
33+
static QString queryFromRect( const QgsRectangle& rect );
34+
35+
QgsOSMDownload();
36+
~QgsOSMDownload();
37+
38+
void setServiceUrl( const QString& serviceUrl ) { mServiceUrl = serviceUrl; }
39+
QString serviceUrl() const { return mServiceUrl; }
40+
41+
void setQuery( const QString& query ) { mQuery = query; }
42+
QString query() const { return mQuery; }
43+
44+
void setOutputFileName( const QString& outputFileName ) { mFile.setFileName( outputFileName ); }
45+
QString outputFileName() const { return mFile.fileName(); }
46+
47+
bool hasError() const { return !mError.isNull(); }
48+
QString errorString() const { return mError; }
49+
50+
/**
51+
* @brief Starts network request for data. The prerequisite is that the query string and output
52+
* file name have been set.
53+
*
54+
* Only one request may be pending at one point - if you need more requests at once, use several instances.
55+
*
56+
* @return true if the network request has been issued, false otherwise (and sets error string)
57+
*/
58+
bool start();
59+
60+
/**
61+
* @brief Aborts current pending request
62+
* @return true if there is a pending request and has been aborted, false otherwise
63+
*/
64+
bool abort();
65+
66+
//! Returns true if the request has already finished
67+
bool isFinished() const;
68+
69+
signals:
70+
void finished(); //!< emitted when the network reply has finished (with success or with an error)
71+
void downloadProgress( qint64, qint64 ); //!< normally the total length is not known (until we reach end)
72+
73+
private slots:
74+
void onReadyRead();
75+
void onFinished();
76+
void onError( QNetworkReply::NetworkError err );
77+
78+
private:
79+
QString mServiceUrl;
80+
QString mQuery;
81+
QString mError;
82+
83+
QNetworkReply* mReply;
84+
QFile mFile;
85+
};
86+
87+
#endif // OSMDOWNLOAD_H
Lines changed: 371 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,371 @@
1+
#include "qgsosmimport.h"
2+
3+
#include <spatialite.h>
4+
5+
#include <QXmlStreamReader>
6+
7+
8+
QgsOSMXmlImport::QgsOSMXmlImport( const QString& xmlFilename, const QString& dbFilename )
9+
: mXmlFileName( xmlFilename )
10+
, mDbFileName( dbFilename )
11+
, mDatabase( 0 )
12+
, mStmtInsertNode( 0 )
13+
, mStmtInsertNodeTag( 0 )
14+
, mStmtInsertWay( 0 )
15+
, mStmtInsertWayNode( 0 )
16+
, mStmtInsertWayTag( 0 )
17+
{
18+
19+
}
20+
21+
bool QgsOSMXmlImport::import()
22+
{
23+
mError.clear();
24+
25+
// open input
26+
mInputFile.setFileName( mXmlFileName );
27+
if ( !mInputFile.open( QIODevice::ReadOnly ) )
28+
{
29+
mError = QString( "Cannot open input file: %1" ).arg( mXmlFileName );
30+
return false;
31+
}
32+
33+
// open output
34+
35+
if ( QFile::exists( mDbFileName ) )
36+
{
37+
if ( !QFile( mDbFileName ).remove() )
38+
{
39+
mError = QString( "Database file cannot be overwritten: %1" ).arg( mDbFileName );
40+
return false;
41+
}
42+
}
43+
44+
// load spatialite extension
45+
spatialite_init( 0 );
46+
47+
if ( !createDatabase() )
48+
{
49+
// mError is set in createDatabase()
50+
return false;
51+
}
52+
53+
qDebug( "starting import" );
54+
55+
int retX = sqlite3_exec( mDatabase, "BEGIN", NULL, NULL, 0 );
56+
Q_ASSERT( retX == SQLITE_OK );
57+
58+
// start parsing
59+
60+
QXmlStreamReader xml( &mInputFile );
61+
62+
while ( !xml.atEnd() )
63+
{
64+
xml.readNext();
65+
66+
if ( xml.isEndDocument() )
67+
break;
68+
69+
if ( xml.isStartElement() )
70+
{
71+
if ( xml.name() == "osm" )
72+
readRoot( xml );
73+
else
74+
xml.raiseError( "Invalid root tag" );
75+
}
76+
}
77+
78+
int retY = sqlite3_exec( mDatabase, "COMMIT", NULL, NULL, 0 );
79+
Q_ASSERT( retY == SQLITE_OK );
80+
81+
createIndexes();
82+
83+
if ( xml.hasError() )
84+
{
85+
mError = QString( "XML error: %1" ).arg( xml.errorString() );
86+
return false;
87+
}
88+
89+
closeDatabase();
90+
91+
return true;
92+
}
93+
94+
bool QgsOSMXmlImport::createIndexes()
95+
{
96+
// index on tags for faster access
97+
const char* sqlIndexes[] =
98+
{
99+
"CREATE INDEX nodes_tags_idx ON nodes_tags(id)",
100+
"CREATE INDEX ways_tags_idx ON ways_tags(id)",
101+
"CREATE INDEX ways_nodes_way ON ways_nodes(way_id)"
102+
};
103+
int count = sizeof( sqlIndexes ) / sizeof( const char* );
104+
for ( int i = 0; i < count; ++i )
105+
{
106+
int ret = sqlite3_exec( mDatabase, sqlIndexes[i], 0, 0, 0 );
107+
if ( ret != SQLITE_OK )
108+
{
109+
mError = "Error creating indexes!";
110+
return false;
111+
}
112+
}
113+
114+
return true;
115+
}
116+
117+
118+
bool QgsOSMXmlImport::createDatabase()
119+
{
120+
if ( sqlite3_open_v2( mDbFileName.toUtf8().data(), &mDatabase, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0 ) != SQLITE_OK )
121+
return false;
122+
123+
const char* sqlInitStatements[] =
124+
{
125+
"PRAGMA cache_size = 100000", // TODO!!!
126+
"PRAGMA synchronous = OFF", // TODO!!!
127+
"SELECT InitSpatialMetadata('WGS84')",
128+
"CREATE TABLE nodes ( id INTEGER PRIMARY KEY, lat REAL, lon REAL )",
129+
"CREATE TABLE nodes_tags ( id INTEGER, k TEXT, v TEXT )",
130+
"CREATE TABLE ways ( id INTEGER PRIMARY KEY )",
131+
"CREATE TABLE ways_nodes ( way_id INTEGER, node_id INTEGER, way_pos INTEGER )",
132+
"CREATE TABLE ways_tags ( id INTEGER, k TEXT, v TEXT )",
133+
};
134+
135+
int initCount = sizeof( sqlInitStatements ) / sizeof( const char* );
136+
for ( int i = 0; i < initCount; ++i )
137+
{
138+
char* errMsg;
139+
if ( sqlite3_exec( mDatabase, sqlInitStatements[i], 0, 0, &errMsg ) != SQLITE_OK )
140+
{
141+
mError = QString( "Error executing SQL command:\n%1\nSQL:\n%2" )
142+
.arg( QString::fromUtf8( errMsg ) ).arg( QString::fromUtf8( sqlInitStatements[i] ) );
143+
sqlite3_free( errMsg );
144+
closeDatabase();
145+
return false;
146+
}
147+
}
148+
149+
const char* sqlInsertStatements[] =
150+
{
151+
"INSERT INTO nodes ( id, lat, lon ) VALUES (?,?,?)",
152+
"INSERT INTO nodes_tags ( id, k, v ) VALUES (?,?,?)",
153+
"INSERT INTO ways ( id ) VALUES (?)",
154+
"INSERT INTO ways_nodes ( way_id, node_id, way_pos ) VALUES (?,?,?)",
155+
"INSERT INTO ways_tags ( id, k, v ) VALUES (?,?,?)"
156+
};
157+
sqlite3_stmt** sqliteInsertStatements[] =
158+
{
159+
&mStmtInsertNode,
160+
&mStmtInsertNodeTag,
161+
&mStmtInsertWay,
162+
&mStmtInsertWayNode,
163+
&mStmtInsertWayTag
164+
};
165+
Q_ASSERT( sizeof( sqlInsertStatements ) / sizeof( const char* ) == sizeof( sqliteInsertStatements ) / sizeof( sqlite3_stmt** ) );
166+
int insertCount = sizeof( sqlInsertStatements ) / sizeof( const char* );
167+
168+
for ( int i = 0; i < insertCount; ++i )
169+
{
170+
if ( sqlite3_prepare_v2( mDatabase, sqlInsertStatements[i], -1, sqliteInsertStatements[i], 0 ) != SQLITE_OK )
171+
{
172+
const char* errMsg = sqlite3_errmsg( mDatabase ); // does not require free
173+
mError = QString( "Error preparing SQL command:\n%1\nSQL:\n%2" )
174+
.arg( QString::fromUtf8( errMsg ) ).arg( QString::fromUtf8( sqlInsertStatements[i] ) );
175+
closeDatabase();
176+
return false;
177+
}
178+
}
179+
180+
return true;
181+
}
182+
183+
184+
void QgsOSMXmlImport::deleteStatement( sqlite3_stmt*& stmt )
185+
{
186+
if ( stmt )
187+
{
188+
sqlite3_finalize( stmt );
189+
stmt = 0;
190+
}
191+
}
192+
193+
194+
bool QgsOSMXmlImport::closeDatabase()
195+
{
196+
if ( !mDatabase )
197+
return false;
198+
199+
deleteStatement( mStmtInsertNode );
200+
deleteStatement( mStmtInsertNodeTag );
201+
deleteStatement( mStmtInsertWay );
202+
deleteStatement( mStmtInsertWayNode );
203+
deleteStatement( mStmtInsertWayTag );
204+
205+
Q_ASSERT( mStmtInsertNode == 0 );
206+
207+
sqlite3_close( mDatabase );
208+
mDatabase = 0;
209+
return true;
210+
}
211+
212+
213+
void QgsOSMXmlImport::readRoot( QXmlStreamReader& xml )
214+
{
215+
int i = 0;
216+
int percent = -1;
217+
218+
while ( !xml.atEnd() )
219+
{
220+
xml.readNext();
221+
222+
if ( xml.isEndElement() ) // </osm>
223+
break;
224+
225+
if ( xml.isStartElement() )
226+
{
227+
if ( ++i == 500 )
228+
{
229+
int new_percent = 100 * mInputFile.pos() / mInputFile.size();
230+
if ( new_percent > percent )
231+
{
232+
emit progress( new_percent );
233+
percent = new_percent;
234+
}
235+
i = 0;
236+
}
237+
238+
if ( xml.name() == "node" )
239+
readNode( xml );
240+
else if ( xml.name() == "way" )
241+
readWay( xml );
242+
else
243+
xml.skipCurrentElement();
244+
}
245+
}
246+
}
247+
248+
249+
void QgsOSMXmlImport::readNode( QXmlStreamReader& xml )
250+
{
251+
// <node id="2197214" lat="50.0682113" lon="14.4348483" user="viduka" uid="595326" visible="true" version="10" changeset="10714591" timestamp="2012-02-17T19:58:49Z">
252+
QXmlStreamAttributes attrs = xml.attributes();
253+
QgsOSMId id = attrs.value( "id" ).toString().toLongLong();
254+
double lat = attrs.value( "lat" ).toString().toDouble();
255+
double lon = attrs.value( "lon" ).toString().toDouble();
256+
257+
// insert to DB
258+
sqlite3_bind_int64( mStmtInsertNode, 1, id );
259+
sqlite3_bind_double( mStmtInsertNode, 2, lat );
260+
sqlite3_bind_double( mStmtInsertNode, 3, lon );
261+
262+
if ( sqlite3_step( mStmtInsertNode ) != SQLITE_DONE )
263+
{
264+
xml.raiseError( QString( "Storing node %1 failed." ).arg( id ) );
265+
}
266+
267+
sqlite3_reset( mStmtInsertNode );
268+
269+
while ( !xml.atEnd() )
270+
{
271+
xml.readNext();
272+
273+
if ( xml.isEndElement() ) // </node>
274+
break;
275+
276+
if ( xml.isStartElement() )
277+
{
278+
if ( xml.name() == "tag" )
279+
readTag( false, id, xml );
280+
else
281+
xml.raiseError( "Invalid tag in <node>" );
282+
}
283+
}
284+
}
285+
286+
void QgsOSMXmlImport::readTag( bool way, QgsOSMId id, QXmlStreamReader& xml )
287+
{
288+
QXmlStreamAttributes attrs = xml.attributes();
289+
QByteArray k = attrs.value( "k" ).toUtf8();
290+
QByteArray v = attrs.value( "v" ).toUtf8();
291+
xml.skipCurrentElement();
292+
293+
sqlite3_stmt* stmtInsertTag = way ? mStmtInsertWayTag : mStmtInsertNodeTag;
294+
295+
sqlite3_bind_int64( stmtInsertTag, 1, id );
296+
sqlite3_bind_text( stmtInsertTag, 2, k.constData(), -1, SQLITE_STATIC );
297+
sqlite3_bind_text( stmtInsertTag, 3, v.constData(), -1, SQLITE_STATIC );
298+
299+
int res = sqlite3_step( stmtInsertTag );
300+
if ( res != SQLITE_DONE )
301+
{
302+
xml.raiseError( QString( "Storing tag failed [%1]" ).arg( res ) );
303+
}
304+
305+
sqlite3_reset( stmtInsertTag );
306+
}
307+
308+
void QgsOSMXmlImport::readWay( QXmlStreamReader& xml )
309+
{
310+
/*
311+
<way id="141756602" user="Vratislav Filler" uid="527259" visible="true" version="1" changeset="10145142" timestamp="2011-12-18T10:43:14Z">
312+
<nd ref="318529958"/>
313+
<nd ref="1551725779"/>
314+
<nd ref="1551725792"/>
315+
<nd ref="809695938"/>
316+
<nd ref="1551725689"/>
317+
<nd ref="809695935"/>
318+
<tag k="highway" v="service"/>
319+
<tag k="oneway" v="yes"/>
320+
</way>
321+
*/
322+
QXmlStreamAttributes attrs = xml.attributes();
323+
QgsOSMId id = attrs.value( "id" ).toString().toLongLong();
324+
325+
// insert to DB
326+
sqlite3_bind_int64( mStmtInsertWay, 1, id );
327+
328+
if ( sqlite3_step( mStmtInsertWay ) != SQLITE_DONE )
329+
{
330+
xml.raiseError( QString( "Storing way %1 failed." ).arg( id ) );
331+
}
332+
333+
sqlite3_reset( mStmtInsertWay );
334+
335+
int way_pos = 0;
336+
337+
while ( !xml.atEnd() )
338+
{
339+
xml.readNext();
340+
341+
if ( xml.isEndElement() ) // </way>
342+
break;
343+
344+
if ( xml.isStartElement() )
345+
{
346+
if ( xml.name() == "nd" )
347+
{
348+
QgsOSMId node_id = xml.attributes().value( "ref" ).toString().toLongLong();
349+
350+
sqlite3_bind_int64( mStmtInsertWayNode, 1, id );
351+
sqlite3_bind_int64( mStmtInsertWayNode, 2, node_id );
352+
sqlite3_bind_int( mStmtInsertWayNode, 3, way_pos );
353+
354+
if ( sqlite3_step( mStmtInsertWayNode ) != SQLITE_DONE )
355+
{
356+
xml.raiseError( QString( "Storing ways_nodes %1 - %2 failed." ).arg( id ).arg( node_id ) );
357+
}
358+
359+
sqlite3_reset( mStmtInsertWayNode );
360+
361+
way_pos++;
362+
363+
xml.skipCurrentElement();
364+
}
365+
else if ( xml.name() == "tag" )
366+
readTag( true, id, xml );
367+
else
368+
xml.skipCurrentElement();
369+
}
370+
}
371+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#ifndef OSMIMPORT_H
2+
#define OSMIMPORT_H
3+
4+
#include <QFile>
5+
#include <QObject>
6+
7+
#include "qgsosmbase.h"
8+
9+
class QXmlStreamReader;
10+
11+
12+
class ANALYSIS_EXPORT QgsOSMXmlImport : public QObject
13+
{
14+
Q_OBJECT
15+
public:
16+
explicit QgsOSMXmlImport( const QString& xmlFileName = QString(), const QString& dbFileName = QString() );
17+
18+
void setInputXmlFileName( const QString& xmlFileName ) { mXmlFileName = xmlFileName; }
19+
QString inputXmlFileName() const { return mXmlFileName; }
20+
21+
void setOutputDbFileName( const QString& dbFileName ) { mDbFileName = dbFileName; }
22+
QString outputDbFileName() const { return mDbFileName; }
23+
24+
bool import();
25+
26+
bool hasError() const { return !mError.isEmpty(); }
27+
QString errorString() const { return mError; }
28+
29+
signals:
30+
void progress( int percent );
31+
32+
protected:
33+
34+
bool createDatabase();
35+
bool closeDatabase();
36+
void deleteStatement( sqlite3_stmt*& stmt );
37+
38+
bool createIndexes();
39+
40+
void readRoot( QXmlStreamReader& xml );
41+
void readNode( QXmlStreamReader& xml );
42+
void readWay( QXmlStreamReader& xml );
43+
void readTag( bool way, QgsOSMId id, QXmlStreamReader& xml );
44+
45+
private:
46+
QString mXmlFileName;
47+
QString mDbFileName;
48+
49+
QString mError;
50+
51+
QFile mInputFile;
52+
53+
sqlite3* mDatabase;
54+
sqlite3_stmt* mStmtInsertNode;
55+
sqlite3_stmt* mStmtInsertNodeTag;
56+
sqlite3_stmt* mStmtInsertWay;
57+
sqlite3_stmt* mStmtInsertWayNode;
58+
sqlite3_stmt* mStmtInsertWayTag;
59+
};
60+
61+
62+
63+
#endif // OSMIMPORT_H

‎src/app/CMakeLists.txt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ SET(QGIS_APP_SRCS
154154

155155
gps/qgsgpsinformationwidget.cpp
156156
gps/qgsgpsmarker.cpp
157+
158+
openstreetmap/qgsosmdownloaddialog.cpp
159+
openstreetmap/qgsosmimportdialog.cpp
160+
openstreetmap/qgsosmexportdialog.cpp
157161
)
158162

159163
IF (ANDROID)
@@ -292,6 +296,10 @@ SET (QGIS_APP_MOC_HDRS
292296
ogr/qgsvectorlayersaveasdialog.h
293297

294298
gps/qgsgpsinformationwidget.h
299+
300+
openstreetmap/qgsosmdownloaddialog.h
301+
openstreetmap/qgsosmimportdialog.h
302+
openstreetmap/qgsosmexportdialog.h
295303
)
296304

297305
IF(WITH_INTERNAL_QWTPOLAR)
@@ -419,14 +427,15 @@ INCLUDE_DIRECTORIES(
419427
${QWT_INCLUDE_DIR}
420428
${QT_QTUITOOLS_INCLUDE_DIR}
421429
${QEXTSERIALPORT_INCLUDE_DIR}
422-
../analysis/raster
430+
../analysis/raster ../analysis/openstreetmap
423431
../core
424432
../core/gps
425433
../core/composer ../core/raster ../core/renderer ../core/symbology ../core/symbology-ng
426434
../gui ../gui/symbology-ng ../gui/attributetable ../gui/raster
427435
../plugins
428436
../python
429437
gps
438+
openstreetmap
430439
)
431440

432441
IF (ANDROID)
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
#include "qgsosmdownloaddialog.h"
2+
3+
#include <QFileDialog>
4+
#include <QMessageBox>
5+
#include <QPushButton>
6+
7+
#include "qgisapp.h"
8+
#include "qgsmapcanvas.h"
9+
#include "qgsmaplayer.h"
10+
#include "qgsmaplayerregistry.h"
11+
#include "qgsrectangle.h"
12+
13+
#include "qgsosmdownload.h"
14+
15+
QgsOSMDownloadDialog::QgsOSMDownloadDialog( QWidget* parent )
16+
: QDialog( parent ), mDownload( new QgsOSMDownload )
17+
{
18+
setupUi( this );
19+
20+
editXMin->setValidator( new QDoubleValidator( -180, 180, 6 ) );
21+
editXMax->setValidator( new QDoubleValidator( -180, 180, 6 ) );
22+
editYMin->setValidator( new QDoubleValidator( -90, 90, 6 ) );
23+
editYMax->setValidator( new QDoubleValidator( -90, 90, 6 ) );
24+
25+
populateLayers();
26+
onExtentCanvas();
27+
28+
connect( radExtentCanvas, SIGNAL( clicked() ), this, SLOT( onExtentCanvas() ) );
29+
connect( radExtentLayer, SIGNAL( clicked() ), this, SLOT( onExtentLayer() ) );
30+
connect( radExtentManual, SIGNAL( clicked() ), this, SLOT( onExtentManual() ) );
31+
connect( cboLayers, SIGNAL( currentIndexChanged( int ) ), this, SLOT( onCurrentLayerChanged( int ) ) );
32+
connect( btnBrowse, SIGNAL( clicked() ), this, SLOT( onBrowseClicked() ) );
33+
connect( buttonBox, SIGNAL( accepted() ), this, SLOT( onOK() ) );
34+
connect( buttonBox, SIGNAL( rejected() ), this, SLOT( onClose() ) );
35+
36+
connect( mDownload, SIGNAL( finished() ), this, SLOT( onFinished() ) );
37+
connect( mDownload, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( onDownloadProgress( qint64, qint64 ) ) );
38+
}
39+
40+
QgsOSMDownloadDialog::~QgsOSMDownloadDialog()
41+
{
42+
delete mDownload;
43+
}
44+
45+
46+
void QgsOSMDownloadDialog::populateLayers()
47+
{
48+
QMap<QString, QgsMapLayer*> layers = QgsMapLayerRegistry::instance()->mapLayers();
49+
QMap<QString, QgsMapLayer*>::iterator it;
50+
for ( it = layers.begin(); it != layers.end(); ++it )
51+
{
52+
cboLayers->addItem( it.value()->name(), it.key() );
53+
}
54+
cboLayers->setCurrentIndex( 0 );
55+
}
56+
57+
void QgsOSMDownloadDialog::setRect( const QgsRectangle& rect )
58+
{
59+
// these coords should be already lat/lon
60+
editXMin->setText( QString::number( rect.xMinimum() ) );
61+
editXMax->setText( QString::number( rect.xMaximum() ) );
62+
editYMin->setText( QString::number( rect.yMinimum() ) );
63+
editYMax->setText( QString::number( rect.yMaximum() ) );
64+
}
65+
66+
QgsRectangle QgsOSMDownloadDialog::rect() const
67+
{
68+
return QgsRectangle( editXMin->text().toDouble(), editYMin->text().toDouble(),
69+
editXMax->text().toDouble(), editYMax->text().toDouble() );
70+
}
71+
72+
73+
void QgsOSMDownloadDialog::setRectReadOnly( bool readonly )
74+
{
75+
editXMin->setReadOnly( readonly );
76+
editXMax->setReadOnly( readonly );
77+
editYMin->setReadOnly( readonly );
78+
editYMax->setReadOnly( readonly );
79+
}
80+
81+
82+
void QgsOSMDownloadDialog::onExtentCanvas()
83+
{
84+
setRect( QgisApp::instance()->mapCanvas()->extent() ); // TODO: transform to WGS84
85+
setRectReadOnly( true );
86+
cboLayers->setEnabled( false );
87+
}
88+
89+
void QgsOSMDownloadDialog::onExtentLayer()
90+
{
91+
onCurrentLayerChanged( cboLayers->currentIndex() );
92+
setRectReadOnly( true );
93+
cboLayers->setEnabled( true );
94+
}
95+
96+
void QgsOSMDownloadDialog::onExtentManual()
97+
{
98+
setRectReadOnly( false );
99+
cboLayers->setEnabled( false );
100+
}
101+
102+
void QgsOSMDownloadDialog::onCurrentLayerChanged( int index )
103+
{
104+
if ( index < 0 )
105+
return;
106+
107+
QString layerId = cboLayers->itemData( index ).toString();
108+
QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( layerId );
109+
if ( !layer )
110+
return;
111+
112+
setRect( layer->extent() ); // TODO: transform to WGS84
113+
}
114+
115+
void QgsOSMDownloadDialog::onBrowseClicked()
116+
{
117+
QSettings settings;
118+
QString lastDir = settings.value( "/osm/lastDir" ).toString();
119+
120+
QString fileName = QFileDialog::getSaveFileName( this, QString(), lastDir, tr( "OpenStreetMap files (*.osm)" ) );
121+
if ( fileName.isNull() )
122+
return;
123+
124+
settings.setValue( "/osm/lastDir", QFileInfo( fileName ).absolutePath() );
125+
editFileName->setText( fileName );
126+
}
127+
128+
void QgsOSMDownloadDialog::onOK()
129+
{
130+
mDownload->setQuery( QgsOSMDownload::queryFromRect( rect() ) );
131+
mDownload->setOutputFileName( editFileName->text() );
132+
if ( !mDownload->start() )
133+
{
134+
QMessageBox::critical( this, tr( "Download error" ), mDownload->errorString() );
135+
return;
136+
}
137+
138+
buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
139+
progress->setRange( 0, 0 ); // this will start animating progress bar
140+
}
141+
142+
void QgsOSMDownloadDialog::onClose()
143+
{
144+
if ( !mDownload->isFinished() )
145+
{
146+
int res = QMessageBox::question( this, tr( "OpenStreetMap download" ),
147+
tr( "Would you like to abort download?" ), QMessageBox::Yes | QMessageBox::No );
148+
if ( res != QMessageBox::Yes )
149+
return;
150+
}
151+
152+
reject();
153+
}
154+
155+
void QgsOSMDownloadDialog::onFinished()
156+
{
157+
buttonBox->button( QDialogButtonBox::Ok )->setEnabled( true );
158+
progress->setRange( 0, 1 );
159+
160+
if ( mDownload->hasError() )
161+
{
162+
QMessageBox::critical( this, tr( "OpenStreetMap download" ), tr( "Download failed.\n%1" ).arg( mDownload->errorString() ) );
163+
}
164+
else
165+
{
166+
QMessageBox::information( this, tr( "OpenStreetMap download" ), tr( "Download has been successful." ) );
167+
}
168+
}
169+
170+
void QgsOSMDownloadDialog::onDownloadProgress( qint64 bytesReceived, qint64 bytesTotal )
171+
{
172+
Q_UNUSED( bytesTotal ); // it's -1 anyway (= unknown)
173+
double mbytesReceived = ( double )bytesReceived / ( 1024 * 1024 );
174+
editSize->setText( QString( "%1 MB" ).arg( QString::number( mbytesReceived, 'f', 1 ) ) );
175+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#ifndef QGSOSMDOWNLOADDIALOG_H
2+
#define QGSOSMDOWNLOADDIALOG_H
3+
4+
#include <QDialog>
5+
6+
#include "ui_qgsosmdownloaddialog.h"
7+
8+
class QgsRectangle;
9+
10+
class QgsOSMDownload;
11+
12+
class QgsOSMDownloadDialog : public QDialog, private Ui::QgsOSMDownloadDialog
13+
{
14+
Q_OBJECT
15+
public:
16+
explicit QgsOSMDownloadDialog( QWidget* parent = 0 );
17+
~QgsOSMDownloadDialog();
18+
19+
void setRect( const QgsRectangle& rect );
20+
void setRectReadOnly( bool readonly );
21+
QgsRectangle rect() const;
22+
23+
private:
24+
void populateLayers();
25+
26+
private slots:
27+
void onExtentCanvas();
28+
void onExtentLayer();
29+
void onExtentManual();
30+
void onCurrentLayerChanged( int index );
31+
void onBrowseClicked();
32+
void onOK();
33+
void onClose();
34+
void onFinished();
35+
void onDownloadProgress( qint64, qint64 );
36+
37+
private:
38+
QgsOSMDownload* mDownload;
39+
};
40+
41+
#endif // QGSOSMDOWNLOADDIALOG_H
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
#include "qgsosmexportdialog.h"
2+
3+
#include "qgsosmdatabase.h"
4+
5+
#include <QApplication>
6+
#include <QFileDialog>
7+
#include <QMessageBox>
8+
#include <QSettings>
9+
#include <QStandardItemModel>
10+
11+
QgsOSMExportDialog::QgsOSMExportDialog( QWidget *parent ) :
12+
QDialog( parent ), mDatabase( new QgsOSMDatabase )
13+
{
14+
setupUi( this );
15+
16+
connect( btnBrowseDb, SIGNAL( clicked() ), this, SLOT( onBrowse() ) );
17+
connect( buttonBox, SIGNAL( accepted() ), this, SLOT( onOK() ) );
18+
connect( buttonBox, SIGNAL( rejected() ), this, SLOT( onClose() ) );
19+
connect( editDbFileName, SIGNAL( textChanged( QString ) ), this, SLOT( updateLayerName() ) );
20+
connect( radPoints, SIGNAL( clicked() ), this, SLOT( updateLayerName() ) );
21+
connect( radPolylines, SIGNAL( clicked() ), this, SLOT( updateLayerName() ) );
22+
connect( radPolygons, SIGNAL( clicked() ), this, SLOT( updateLayerName() ) );
23+
connect( btnLoadTags, SIGNAL( clicked() ), this, SLOT( onLoadTags() ) );
24+
25+
mTagsModel = new QStandardItemModel( this );
26+
mTagsModel->setHorizontalHeaderLabels( QStringList() << tr( "Tag" ) << tr( "Count" ) );
27+
viewTags->setModel( mTagsModel );
28+
}
29+
30+
QgsOSMExportDialog::~QgsOSMExportDialog()
31+
{
32+
delete mDatabase;
33+
}
34+
35+
36+
void QgsOSMExportDialog::onBrowse()
37+
{
38+
QSettings settings;
39+
QString lastDir = settings.value( "/osm/lastDir" ).toString();
40+
41+
QString fileName = QFileDialog::getOpenFileName( this, QString(), lastDir, tr( "SQLite databases (*.db)" ) );
42+
if ( fileName.isNull() )
43+
return;
44+
45+
settings.setValue( "/osm/lastDir", QFileInfo( fileName ).absolutePath() );
46+
editDbFileName->setText( fileName );
47+
}
48+
49+
void QgsOSMExportDialog::updateLayerName()
50+
{
51+
QString baseName = QFileInfo( editDbFileName->text() ).baseName();
52+
53+
QString layerType;
54+
if ( radPoints->isChecked() )
55+
layerType = "points";
56+
else if ( radPolylines->isChecked() )
57+
layerType = "polylines";
58+
else
59+
layerType = "polygons";
60+
editLayerName->setText( QString( "%1_%2" ).arg( baseName ).arg( layerType ) );
61+
}
62+
63+
64+
bool QgsOSMExportDialog::openDatabase()
65+
{
66+
mDatabase->setFileName( editDbFileName->text() );
67+
68+
if ( !mDatabase->open() )
69+
{
70+
QMessageBox::critical( this, QString(), tr( "Unable to open database:\n%1" ).arg( mDatabase->errorString() ) );
71+
return false;
72+
}
73+
74+
return true;
75+
}
76+
77+
78+
void QgsOSMExportDialog::onLoadTags()
79+
{
80+
if ( !openDatabase() )
81+
return;
82+
83+
QApplication::setOverrideCursor( Qt::WaitCursor );
84+
85+
QList<QgsOSMTagCountPair> pairs = mDatabase->usedTags( !radPoints->isChecked() );
86+
mDatabase->close();
87+
88+
mTagsModel->setColumnCount( 2 );
89+
mTagsModel->setRowCount( pairs.count() );
90+
91+
for ( int i = 0; i < pairs.count(); ++i )
92+
{
93+
const QgsOSMTagCountPair& p = pairs[i];
94+
QStandardItem* item = new QStandardItem( p.first );
95+
item->setCheckable( true );
96+
mTagsModel->setItem( i, 0, item );
97+
QStandardItem* item2 = new QStandardItem();
98+
item2->setData( p.second, Qt::DisplayRole );
99+
mTagsModel->setItem( i, 1, item2 );
100+
}
101+
102+
viewTags->resizeColumnToContents( 0 );
103+
viewTags->sortByColumn( 1, Qt::DescendingOrder );
104+
105+
QApplication::restoreOverrideCursor();
106+
}
107+
108+
109+
void QgsOSMExportDialog::onOK()
110+
{
111+
if ( !openDatabase() )
112+
return;
113+
114+
QgsOSMDatabase::ExportType type;
115+
if ( radPoints->isChecked() )
116+
type = QgsOSMDatabase::Point;
117+
else if ( radPolylines->isChecked() )
118+
type = QgsOSMDatabase::Polyline;
119+
else
120+
type = QgsOSMDatabase::Polygon;
121+
122+
buttonBox->setEnabled( false );
123+
QApplication::setOverrideCursor( Qt::WaitCursor );
124+
125+
QStringList tagKeys;
126+
127+
for ( int i = 0; i < mTagsModel->rowCount(); ++i )
128+
{
129+
QStandardItem* item = mTagsModel->item( i, 0 );
130+
if ( item->checkState() == Qt::Checked )
131+
tagKeys << item->text();
132+
}
133+
134+
bool res = mDatabase->exportSpatiaLite( type, editLayerName->text(), tagKeys );
135+
136+
QApplication::restoreOverrideCursor();
137+
buttonBox->setEnabled( true );
138+
139+
if ( res )
140+
{
141+
QMessageBox::information( this, tr( "OpenStreetMap export" ), tr( "Export has been successful." ) );
142+
}
143+
else
144+
{
145+
QMessageBox::critical( this, tr( "OpenStreetMap import" ), tr( "Failed to export OSM data:\n%1" ).arg( mDatabase->errorString() ) );
146+
}
147+
148+
mDatabase->close();
149+
}
150+
151+
void QgsOSMExportDialog::onClose()
152+
{
153+
reject();
154+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#ifndef QGSOSMEXPORTDIALOG_H
2+
#define QGSOSMEXPORTDIALOG_H
3+
4+
#include <QDialog>
5+
6+
#include "ui_qgsosmexportdialog.h"
7+
8+
class QgsOSMDatabase;
9+
10+
class QStandardItemModel;
11+
12+
class QgsOSMExportDialog : public QDialog, private Ui::QgsOSMExportDialog
13+
{
14+
Q_OBJECT
15+
public:
16+
explicit QgsOSMExportDialog( QWidget *parent = 0 );
17+
~QgsOSMExportDialog();
18+
19+
protected:
20+
bool openDatabase();
21+
22+
private slots:
23+
void onBrowse();
24+
void updateLayerName();
25+
void onLoadTags();
26+
27+
void onOK();
28+
void onClose();
29+
30+
private:
31+
QgsOSMDatabase* mDatabase;
32+
QStandardItemModel* mTagsModel;
33+
};
34+
35+
#endif // QGSOSMEXPORTDIALOG_H
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#include "qgsosmimportdialog.h"
2+
3+
#include <QApplication>
4+
#include <QFileDialog>
5+
#include <QMessageBox>
6+
#include <QSettings>
7+
8+
#include "qgsosmimport.h"
9+
10+
QgsOSMImportDialog::QgsOSMImportDialog( QWidget* parent )
11+
: QDialog( parent ), mImport( new QgsOSMXmlImport )
12+
{
13+
setupUi( this );
14+
15+
connect( btnBrowseXml, SIGNAL( clicked() ), this, SLOT( onBrowseXml() ) );
16+
connect( btnBrowseDb, SIGNAL( clicked() ), this, SLOT( onBrowseDb() ) );
17+
connect( editXmlFileName, SIGNAL( textChanged( const QString& ) ), this, SLOT( xmlFileNameChanged( const QString& ) ) );
18+
connect( editDbFileName, SIGNAL( textChanged( const QString& ) ), this, SLOT( dbFileNameChanged( const QString& ) ) );
19+
connect( buttonBox, SIGNAL( accepted() ), this, SLOT( onOK() ) );
20+
connect( buttonBox, SIGNAL( rejected() ), this, SLOT( onClose() ) );
21+
22+
connect( mImport, SIGNAL( progress( int ) ), this, SLOT( onProgress( int ) ) );
23+
}
24+
25+
QgsOSMImportDialog::~QgsOSMImportDialog()
26+
{
27+
delete mImport;
28+
}
29+
30+
31+
void QgsOSMImportDialog::onBrowseXml()
32+
{
33+
QSettings settings;
34+
QString lastDir = settings.value( "/osm/lastDir" ).toString();
35+
36+
QString fileName = QFileDialog::getOpenFileName( this, QString(), lastDir, tr( "OpenStreetMap files (*.osm)" ) );
37+
if ( fileName.isNull() )
38+
return;
39+
40+
settings.setValue( "/osm/lastDir", QFileInfo( fileName ).absolutePath() );
41+
editXmlFileName->setText( fileName );
42+
}
43+
44+
void QgsOSMImportDialog::onBrowseDb()
45+
{
46+
QSettings settings;
47+
QString lastDir = settings.value( "/osm/lastDir" ).toString();
48+
49+
QString fileName = QFileDialog::getSaveFileName( this, QString(), lastDir, tr( "SQLite databases (*.db)" ) );
50+
if ( fileName.isNull() )
51+
return;
52+
53+
settings.setValue( "/osm/lastDir", QFileInfo( fileName ).absolutePath() );
54+
editDbFileName->setText( fileName );
55+
}
56+
57+
58+
void QgsOSMImportDialog::xmlFileNameChanged( const QString& fileName )
59+
{
60+
editDbFileName->setText( fileName + ".db" );
61+
}
62+
63+
void QgsOSMImportDialog::dbFileNameChanged( const QString& fileName )
64+
{
65+
editConnName->setText( QFileInfo( fileName ).baseName() );
66+
}
67+
68+
void QgsOSMImportDialog::onOK()
69+
{
70+
// output file exists?
71+
if ( QFileInfo( editDbFileName->text() ).exists() )
72+
{
73+
int res = QMessageBox::question( this, tr( "OpenStreetMap import" ), tr( "Output database file exists already. Overwrite?" ), QMessageBox::Yes | QMessageBox::No );
74+
if ( res != QMessageBox::Yes )
75+
return;
76+
}
77+
78+
mImport->setInputXmlFileName( editXmlFileName->text() );
79+
mImport->setOutputDbFileName( editDbFileName->text() );
80+
81+
buttonBox->setEnabled( false );
82+
QApplication::setOverrideCursor( Qt::WaitCursor );
83+
84+
bool res = mImport->import();
85+
86+
QApplication::restoreOverrideCursor();
87+
buttonBox->setEnabled( true );
88+
89+
progressBar->setValue( 0 );
90+
91+
if ( !res )
92+
{
93+
QMessageBox::critical( this, tr( "OpenStreetMap import" ), tr( "Failed to import import OSM data:\n%1" ).arg( mImport->errorString() ) );
94+
return;
95+
}
96+
97+
if ( groupCreateConn->isChecked() )
98+
{
99+
// create connection - this is a bit hacky, sorry for that.
100+
QSettings settings;
101+
settings.setValue( QString( "/SpatiaLite/connections/%1/sqlitepath" ).arg( editConnName->text() ), mImport->outputDbFileName() );
102+
}
103+
104+
QMessageBox::information( this, tr( "OpenStreetMap import" ), tr( "Import has been successful." ) );
105+
}
106+
107+
void QgsOSMImportDialog::onClose()
108+
{
109+
reject();
110+
}
111+
112+
void QgsOSMImportDialog::onProgress( int percent )
113+
{
114+
progressBar->setValue( percent );
115+
qApp->processEvents( QEventLoop::ExcludeSocketNotifiers );
116+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#ifndef QGSOSMIMPORTDIALOG_H
2+
#define QGSOSMIMPORTDIALOG_H
3+
4+
#include <QDialog>
5+
6+
#include "ui_qgsosmimportdialog.h"
7+
8+
class QgsOSMXmlImport;
9+
10+
class QgsOSMImportDialog : public QDialog, private Ui::QgsOSMImportDialog
11+
{
12+
Q_OBJECT
13+
public:
14+
explicit QgsOSMImportDialog( QWidget* parent = 0 );
15+
~QgsOSMImportDialog();
16+
17+
private slots:
18+
void onBrowseXml();
19+
void onBrowseDb();
20+
21+
void xmlFileNameChanged( const QString& fileName );
22+
void dbFileNameChanged( const QString& fileName );
23+
24+
void onOK();
25+
void onClose();
26+
27+
void onProgress( int percent );
28+
29+
private:
30+
QgsOSMXmlImport* mImport;
31+
};
32+
33+
#endif // QGSOSMIMPORTDIALOG_H

‎src/app/qgisapp.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,11 @@
192192
#include "ogr/qgsogrsublayersdialog.h"
193193
#include "ogr/qgsopenvectorlayerdialog.h"
194194
#include "ogr/qgsvectorlayersaveasdialog.h"
195+
196+
#include "qgsosmdownloaddialog.h"
197+
#include "qgsosmimportdialog.h"
198+
#include "qgsosmexportdialog.h"
199+
195200
//
196201
// GDAL/OGR includes
197202
//
@@ -525,6 +530,16 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, QWidget * parent,
525530
mSaveRollbackInProgress = false;
526531
activateDeactivateLayerRelatedActions( NULL );
527532

533+
QAction* actionOSMDownload = new QAction( tr( "Download data" ), this );
534+
connect( actionOSMDownload, SIGNAL( triggered() ), this, SLOT( osmDownloadDialog() ) );
535+
QAction* actionOSMImport = new QAction( tr( "Import topology from XML" ), this );
536+
connect( actionOSMImport, SIGNAL( triggered() ), this, SLOT( osmImportDialog() ) );
537+
QAction* actionOSMExport = new QAction( tr( "Export topology to SpatiaLite" ), this );
538+
connect( actionOSMExport, SIGNAL( triggered() ), this, SLOT( osmExportDialog() ) );
539+
addPluginToVectorMenu( "OpenStreetMap", actionOSMDownload );
540+
addPluginToVectorMenu( "OpenStreetMap", actionOSMImport );
541+
addPluginToVectorMenu( "OpenStreetMap", actionOSMExport );
542+
528543
addDockWidget( Qt::LeftDockWidgetArea, mUndoWidget );
529544
mUndoWidget->hide();
530545

@@ -8868,6 +8883,25 @@ QMenu* QgisApp::createPopupMenu()
88688883
return menu;
88698884
}
88708885

8886+
void QgisApp::osmDownloadDialog()
8887+
{
8888+
QgsOSMDownloadDialog dlg;
8889+
dlg.exec();
8890+
}
8891+
8892+
void QgisApp::osmImportDialog()
8893+
{
8894+
QgsOSMImportDialog dlg;
8895+
dlg.exec();
8896+
}
8897+
8898+
void QgisApp::osmExportDialog()
8899+
{
8900+
QgsOSMExportDialog dlg;
8901+
dlg.exec();
8902+
}
8903+
8904+
88718905
#ifdef HAVE_TOUCH
88728906
bool QgisApp::gestureEvent( QGestureEvent *event )
88738907
{

‎src/app/qgisapp.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1019,6 +1019,10 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
10191019
//! trust and load project macros
10201020
void enableProjectMacros();
10211021

1022+
void osmDownloadDialog();
1023+
void osmImportDialog();
1024+
void osmExportDialog();
1025+
10221026
signals:
10231027
/** emitted when a key is pressed and we want non widget sublasses to be able
10241028
to pick up on this (e.g. maplayer) */

‎src/ui/qgsosmdownloaddialog.ui

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>QgsOSMDownloadDialog</class>
4+
<widget class="QDialog" name="QgsOSMDownloadDialog">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>398</width>
10+
<height>312</height>
11+
</rect>
12+
</property>
13+
<property name="windowTitle">
14+
<string>Download OpenStreetMap data</string>
15+
</property>
16+
<layout class="QVBoxLayout" name="verticalLayout_2">
17+
<item>
18+
<widget class="QGroupBox" name="groupBox">
19+
<property name="title">
20+
<string>Extent</string>
21+
</property>
22+
<layout class="QVBoxLayout" name="verticalLayout">
23+
<item>
24+
<widget class="QRadioButton" name="radExtentCanvas">
25+
<property name="text">
26+
<string>From map canvas</string>
27+
</property>
28+
<property name="checked">
29+
<bool>true</bool>
30+
</property>
31+
</widget>
32+
</item>
33+
<item>
34+
<layout class="QHBoxLayout" name="horizontalLayout_2">
35+
<item>
36+
<widget class="QRadioButton" name="radExtentLayer">
37+
<property name="text">
38+
<string>From layer</string>
39+
</property>
40+
</widget>
41+
</item>
42+
<item>
43+
<widget class="QComboBox" name="cboLayers"/>
44+
</item>
45+
</layout>
46+
</item>
47+
<item>
48+
<widget class="QRadioButton" name="radExtentManual">
49+
<property name="text">
50+
<string>Manual</string>
51+
</property>
52+
</widget>
53+
</item>
54+
<item>
55+
<layout class="QGridLayout" name="gridLayout">
56+
<item row="0" column="0">
57+
<spacer name="horizontalSpacer_4">
58+
<property name="orientation">
59+
<enum>Qt::Horizontal</enum>
60+
</property>
61+
<property name="sizeHint" stdset="0">
62+
<size>
63+
<width>40</width>
64+
<height>20</height>
65+
</size>
66+
</property>
67+
</spacer>
68+
</item>
69+
<item row="0" column="1" colspan="2">
70+
<widget class="QLineEdit" name="editYMax"/>
71+
</item>
72+
<item row="0" column="3">
73+
<spacer name="horizontalSpacer_3">
74+
<property name="orientation">
75+
<enum>Qt::Horizontal</enum>
76+
</property>
77+
<property name="sizeHint" stdset="0">
78+
<size>
79+
<width>40</width>
80+
<height>20</height>
81+
</size>
82+
</property>
83+
</spacer>
84+
</item>
85+
<item row="1" column="0" colspan="2">
86+
<widget class="QLineEdit" name="editXMin"/>
87+
</item>
88+
<item row="1" column="2" colspan="2">
89+
<widget class="QLineEdit" name="editXMax"/>
90+
</item>
91+
<item row="2" column="0">
92+
<spacer name="horizontalSpacer">
93+
<property name="orientation">
94+
<enum>Qt::Horizontal</enum>
95+
</property>
96+
<property name="sizeHint" stdset="0">
97+
<size>
98+
<width>40</width>
99+
<height>20</height>
100+
</size>
101+
</property>
102+
</spacer>
103+
</item>
104+
<item row="2" column="1" colspan="2">
105+
<widget class="QLineEdit" name="editYMin"/>
106+
</item>
107+
<item row="2" column="3">
108+
<spacer name="horizontalSpacer_2">
109+
<property name="orientation">
110+
<enum>Qt::Horizontal</enum>
111+
</property>
112+
<property name="sizeHint" stdset="0">
113+
<size>
114+
<width>40</width>
115+
<height>20</height>
116+
</size>
117+
</property>
118+
</spacer>
119+
</item>
120+
</layout>
121+
</item>
122+
</layout>
123+
</widget>
124+
</item>
125+
<item>
126+
<widget class="QGroupBox" name="groupBox_2">
127+
<property name="title">
128+
<string>Output file</string>
129+
</property>
130+
<layout class="QHBoxLayout" name="horizontalLayout">
131+
<item>
132+
<widget class="QLineEdit" name="editFileName"/>
133+
</item>
134+
<item>
135+
<widget class="QToolButton" name="btnBrowse">
136+
<property name="text">
137+
<string>...</string>
138+
</property>
139+
</widget>
140+
</item>
141+
</layout>
142+
</widget>
143+
</item>
144+
<item>
145+
<layout class="QHBoxLayout" name="horizontalLayout_3">
146+
<item>
147+
<widget class="QLineEdit" name="editSize">
148+
<property name="readOnly">
149+
<bool>true</bool>
150+
</property>
151+
</widget>
152+
</item>
153+
<item>
154+
<widget class="QProgressBar" name="progress"/>
155+
</item>
156+
<item>
157+
<widget class="QDialogButtonBox" name="buttonBox">
158+
<property name="orientation">
159+
<enum>Qt::Horizontal</enum>
160+
</property>
161+
<property name="standardButtons">
162+
<set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set>
163+
</property>
164+
</widget>
165+
</item>
166+
</layout>
167+
</item>
168+
</layout>
169+
</widget>
170+
<tabstops>
171+
<tabstop>radExtentCanvas</tabstop>
172+
<tabstop>radExtentLayer</tabstop>
173+
<tabstop>cboLayers</tabstop>
174+
<tabstop>radExtentManual</tabstop>
175+
<tabstop>editYMax</tabstop>
176+
<tabstop>editXMin</tabstop>
177+
<tabstop>editXMax</tabstop>
178+
<tabstop>editYMin</tabstop>
179+
<tabstop>editFileName</tabstop>
180+
<tabstop>btnBrowse</tabstop>
181+
<tabstop>buttonBox</tabstop>
182+
</tabstops>
183+
<resources/>
184+
<connections/>
185+
</ui>

‎src/ui/qgsosmexportdialog.ui

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>QgsOSMExportDialog</class>
4+
<widget class="QDialog" name="QgsOSMExportDialog">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>458</width>
10+
<height>436</height>
11+
</rect>
12+
</property>
13+
<property name="windowTitle">
14+
<string>Export OpenStreetMap topology to SpatiaLite</string>
15+
</property>
16+
<layout class="QVBoxLayout" name="verticalLayout_3">
17+
<item>
18+
<widget class="QGroupBox" name="groupBox">
19+
<property name="title">
20+
<string>Input DB file</string>
21+
</property>
22+
<layout class="QHBoxLayout" name="horizontalLayout_3">
23+
<item>
24+
<widget class="QLineEdit" name="editDbFileName"/>
25+
</item>
26+
<item>
27+
<widget class="QToolButton" name="btnBrowseDb">
28+
<property name="text">
29+
<string>...</string>
30+
</property>
31+
</widget>
32+
</item>
33+
</layout>
34+
</widget>
35+
</item>
36+
<item>
37+
<widget class="QGroupBox" name="groupBox_4">
38+
<property name="title">
39+
<string>Export type</string>
40+
</property>
41+
<layout class="QHBoxLayout" name="horizontalLayout_2">
42+
<item>
43+
<widget class="QRadioButton" name="radPoints">
44+
<property name="text">
45+
<string>Points (nodes)</string>
46+
</property>
47+
<property name="checked">
48+
<bool>true</bool>
49+
</property>
50+
</widget>
51+
</item>
52+
<item>
53+
<widget class="QRadioButton" name="radPolylines">
54+
<property name="text">
55+
<string>Polylines (open ways)</string>
56+
</property>
57+
</widget>
58+
</item>
59+
<item>
60+
<widget class="QRadioButton" name="radPolygons">
61+
<property name="text">
62+
<string>Polygons (closed ways)</string>
63+
</property>
64+
</widget>
65+
</item>
66+
</layout>
67+
</widget>
68+
</item>
69+
<item>
70+
<widget class="QGroupBox" name="groupBox_2">
71+
<property name="title">
72+
<string>Output layer name</string>
73+
</property>
74+
<layout class="QVBoxLayout" name="verticalLayout">
75+
<item>
76+
<widget class="QLineEdit" name="editLayerName"/>
77+
</item>
78+
</layout>
79+
</widget>
80+
</item>
81+
<item>
82+
<widget class="QGroupBox" name="groupBox_3">
83+
<property name="title">
84+
<string>Exported tags</string>
85+
</property>
86+
<layout class="QVBoxLayout" name="verticalLayout_2">
87+
<item>
88+
<layout class="QHBoxLayout" name="horizontalLayout">
89+
<item>
90+
<widget class="QPushButton" name="btnLoadTags">
91+
<property name="text">
92+
<string>Load from DB</string>
93+
</property>
94+
</widget>
95+
</item>
96+
<item>
97+
<spacer name="horizontalSpacer">
98+
<property name="orientation">
99+
<enum>Qt::Horizontal</enum>
100+
</property>
101+
<property name="sizeHint" stdset="0">
102+
<size>
103+
<width>40</width>
104+
<height>20</height>
105+
</size>
106+
</property>
107+
</spacer>
108+
</item>
109+
</layout>
110+
</item>
111+
<item>
112+
<widget class="QTreeView" name="viewTags">
113+
<property name="sortingEnabled">
114+
<bool>true</bool>
115+
</property>
116+
</widget>
117+
</item>
118+
</layout>
119+
</widget>
120+
</item>
121+
<item>
122+
<layout class="QHBoxLayout" name="horizontalLayout_4">
123+
<item>
124+
<widget class="QProgressBar" name="progressBar">
125+
<property name="value">
126+
<number>0</number>
127+
</property>
128+
</widget>
129+
</item>
130+
<item>
131+
<widget class="QDialogButtonBox" name="buttonBox">
132+
<property name="orientation">
133+
<enum>Qt::Horizontal</enum>
134+
</property>
135+
<property name="standardButtons">
136+
<set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set>
137+
</property>
138+
</widget>
139+
</item>
140+
</layout>
141+
</item>
142+
</layout>
143+
</widget>
144+
<tabstops>
145+
<tabstop>editDbFileName</tabstop>
146+
<tabstop>btnBrowseDb</tabstop>
147+
<tabstop>radPoints</tabstop>
148+
<tabstop>radPolylines</tabstop>
149+
<tabstop>radPolygons</tabstop>
150+
<tabstop>editLayerName</tabstop>
151+
<tabstop>btnLoadTags</tabstop>
152+
<tabstop>viewTags</tabstop>
153+
<tabstop>buttonBox</tabstop>
154+
</tabstops>
155+
<resources/>
156+
<connections/>
157+
</ui>

‎src/ui/qgsosmimportdialog.ui

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>QgsOSMImportDialog</class>
4+
<widget class="QDialog" name="QgsOSMImportDialog">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>396</width>
10+
<height>257</height>
11+
</rect>
12+
</property>
13+
<property name="windowTitle">
14+
<string>OpenStreetMap Import</string>
15+
</property>
16+
<layout class="QVBoxLayout" name="verticalLayout">
17+
<item>
18+
<widget class="QGroupBox" name="groupBox_2">
19+
<property name="title">
20+
<string>Input XML file (.osm)</string>
21+
</property>
22+
<layout class="QHBoxLayout" name="horizontalLayout">
23+
<item>
24+
<widget class="QLineEdit" name="editXmlFileName"/>
25+
</item>
26+
<item>
27+
<widget class="QToolButton" name="btnBrowseXml">
28+
<property name="text">
29+
<string>...</string>
30+
</property>
31+
</widget>
32+
</item>
33+
</layout>
34+
</widget>
35+
</item>
36+
<item>
37+
<widget class="QGroupBox" name="groupBox">
38+
<property name="title">
39+
<string>Output SpatiaLite DB file</string>
40+
</property>
41+
<layout class="QHBoxLayout" name="horizontalLayout_3">
42+
<item>
43+
<widget class="QLineEdit" name="editDbFileName"/>
44+
</item>
45+
<item>
46+
<widget class="QToolButton" name="btnBrowseDb">
47+
<property name="text">
48+
<string>...</string>
49+
</property>
50+
</widget>
51+
</item>
52+
</layout>
53+
</widget>
54+
</item>
55+
<item>
56+
<widget class="QGroupBox" name="groupCreateConn">
57+
<property name="title">
58+
<string>Create connection (SpatiaLite) after import</string>
59+
</property>
60+
<property name="checkable">
61+
<bool>true</bool>
62+
</property>
63+
<layout class="QFormLayout" name="formLayout">
64+
<item row="0" column="0">
65+
<widget class="QLabel" name="label">
66+
<property name="text">
67+
<string>Connection name</string>
68+
</property>
69+
</widget>
70+
</item>
71+
<item row="0" column="1">
72+
<widget class="QLineEdit" name="editConnName"/>
73+
</item>
74+
</layout>
75+
</widget>
76+
</item>
77+
<item>
78+
<spacer name="verticalSpacer">
79+
<property name="orientation">
80+
<enum>Qt::Vertical</enum>
81+
</property>
82+
<property name="sizeHint" stdset="0">
83+
<size>
84+
<width>20</width>
85+
<height>21</height>
86+
</size>
87+
</property>
88+
</spacer>
89+
</item>
90+
<item>
91+
<layout class="QHBoxLayout" name="horizontalLayout_2">
92+
<item>
93+
<widget class="QProgressBar" name="progressBar">
94+
<property name="value">
95+
<number>0</number>
96+
</property>
97+
</widget>
98+
</item>
99+
<item>
100+
<widget class="QDialogButtonBox" name="buttonBox">
101+
<property name="orientation">
102+
<enum>Qt::Horizontal</enum>
103+
</property>
104+
<property name="standardButtons">
105+
<set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set>
106+
</property>
107+
</widget>
108+
</item>
109+
</layout>
110+
</item>
111+
</layout>
112+
</widget>
113+
<tabstops>
114+
<tabstop>editXmlFileName</tabstop>
115+
<tabstop>btnBrowseXml</tabstop>
116+
<tabstop>editDbFileName</tabstop>
117+
<tabstop>btnBrowseDb</tabstop>
118+
<tabstop>groupCreateConn</tabstop>
119+
<tabstop>editConnName</tabstop>
120+
<tabstop>buttonBox</tabstop>
121+
</tabstops>
122+
<resources/>
123+
<connections/>
124+
</ui>

‎tests/src/analysis/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ ENDMACRO (ADD_QGIS_TEST)
7272
# Tests:
7373

7474
ADD_QGIS_TEST(analyzertest testqgsvectoranalyzer.cpp)
75+
ADD_QGIS_TEST(openstreetmaptest testopenstreetmap.cpp)
7576

7677

7778

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/***************************************************************************
2+
testopenstreetmap.cpp
3+
--------------------------------------
4+
Date : January 2013
5+
Copyright : (C) 2013 by Martin Dobias
6+
Email : wonder dot sk at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include <QtTest>
17+
#include <QSignalSpy>
18+
19+
#include <qgsapplication.h>
20+
//#include <qgsproviderregistry.h>
21+
22+
#include "openstreetmap/qgsosmdatabase.h"
23+
#include "openstreetmap/qgsosmdownload.h"
24+
#include "openstreetmap/qgsosmimport.h"
25+
26+
class TestOpenStreetMap : public QObject
27+
{
28+
Q_OBJECT
29+
private slots:
30+
void initTestCase();// will be called before the first testfunction is executed.
31+
void cleanupTestCase();// will be called after the last testfunction was executed.
32+
void init() ;// will be called before each testfunction is executed.
33+
void cleanup() ;// will be called after every testfunction.
34+
/** Our tests proper begin here */
35+
void download();
36+
void importAndQueries();
37+
private:
38+
39+
};
40+
41+
void TestOpenStreetMap::initTestCase()
42+
{
43+
//
44+
// Runs once before any tests are run
45+
//
46+
// init QGIS's paths - true means that all path will be inited from prefix
47+
QgsApplication::init();
48+
//QgsApplication::initQgis();
49+
//QgsApplication::showSettings();
50+
51+
//create some objects that will be used in all tests...
52+
//create a map layer that will be used in all tests...
53+
//QString myBaseFileName( TEST_DATA_DIR ); //defined in CmakeLists.txt
54+
}
55+
void TestOpenStreetMap::cleanupTestCase()
56+
{
57+
58+
}
59+
void TestOpenStreetMap::init()
60+
{
61+
62+
}
63+
void TestOpenStreetMap::cleanup()
64+
{
65+
}
66+
67+
68+
void TestOpenStreetMap::download()
69+
{
70+
QgsRectangle rect( 7.148, 51.249, 7.152, 51.251 );
71+
72+
// start download
73+
OSMDownload download;
74+
download.setQuery( OSMDownload::queryFromRect( rect ) );
75+
download.setOutputFileName( "/tmp/dl-test.osm" );
76+
bool res = download.start();
77+
QVERIFY( res );
78+
79+
// wait for finished() signal
80+
int timeout = 15000; // in miliseconds - max waiting time
81+
int waitTime = 500; // in miliseconds - unit waiting time
82+
QSignalSpy spy( &download, SIGNAL( finished() ) );
83+
while ( timeout > 0 && spy.count() == 0 )
84+
{
85+
QTest::qWait( waitTime );
86+
timeout -= waitTime;
87+
}
88+
89+
QVERIFY( spy.count() != 0 );
90+
91+
if ( download.hasError() )
92+
qDebug( "ERROR: %s", download.errorString().toAscii().data() );
93+
}
94+
95+
96+
void TestOpenStreetMap::importAndQueries()
97+
{
98+
QString dbFilename = "/tmp/testdata.db";
99+
//QString xmlFilename = "/tmp/130127_023233_downloaded.osm";
100+
QString xmlFilename = TEST_DATA_DIR "/openstreetmap/testdata.xml";
101+
102+
QgsOSMXmlImport import( xmlFilename, dbFilename );
103+
bool res = import.import();
104+
if ( import.hasError() )
105+
qDebug( "XML ERR: %s", import.errorString().toAscii().data() );
106+
QCOMPARE( res, true );
107+
QCOMPARE( import.hasError(), false );
108+
109+
qDebug( "import finished" );
110+
111+
QgsOSMDatabase db( dbFilename );
112+
bool dbopenRes = db.open();
113+
if ( !db.errorString().isEmpty() )
114+
qDebug( "DB ERR: %s", db.errorString().toAscii().data() );
115+
QCOMPARE( dbopenRes, true );
116+
117+
// query node
118+
119+
QgsOSMNode n = db.node( 11111 );
120+
QCOMPARE( n.isValid(), true );
121+
QCOMPARE( n.point().x(), 14.4277148 );
122+
QCOMPARE( n.point().y(), 50.0651387 );
123+
124+
QgsOSMNode nNot = db.node( 22222 );
125+
QCOMPARE( nNot.isValid(), false );
126+
127+
// query node tags
128+
129+
QgsOSMTags tags = db.tags( false, 11111 );
130+
QCOMPARE( tags.count(), 7 );
131+
QCOMPARE( tags.value( "addr:postcode" ), QString( "12800" ) );
132+
133+
QgsOSMTags tags2 = db.tags( false, 360769661 );
134+
QCOMPARE( tags2.count(), 0 );
135+
QCOMPARE( tags2.value( "addr:postcode" ), QString() );
136+
137+
QgsOSMTags tagsNot = db.tags( false, 22222 );
138+
QCOMPARE( tagsNot.count(), 0 );
139+
140+
// list nodes
141+
142+
QgsOSMNodeIterator nodes = db.listNodes();
143+
QCOMPARE( nodes.next().id(), 11111 );
144+
QCOMPARE( nodes.next().id(), 360769661 );
145+
nodes.close();
146+
147+
// query way
148+
149+
QgsOSMWay w = db.way( 32137532 );
150+
QCOMPARE( w.isValid(), true );
151+
QCOMPARE( w.nodes().count(), 5 );
152+
QCOMPARE( w.nodes()[0], ( qint64 )360769661 );
153+
QCOMPARE( w.nodes()[1], ( qint64 )360769664 );
154+
155+
QgsOSMWay wNot = db.way( 1234567 );
156+
QCOMPARE( wNot.isValid(), false );
157+
158+
// query way tags
159+
160+
QgsOSMTags tagsW = db.tags( true, 32137532 );
161+
QCOMPARE( tagsW.count(), 3 );
162+
QCOMPARE( tagsW.value( "building" ), QString( "yes" ) );
163+
164+
QgsOSMTags tagsWNot = db.tags( true, 1234567 );
165+
QCOMPARE( tagsWNot.count(), 0 );
166+
167+
// list ways
168+
169+
QgsOSMWayIterator ways = db.listWays();
170+
QCOMPARE( ways.next().id(), 32137532 );
171+
QCOMPARE( ways.next().isValid(), false );
172+
ways.close();
173+
174+
bool exportRes1 = db.exportSpatiaLite( QgsOSMDatabase::Point, "sl_points", QStringList( "addr:postcode" ) );
175+
//bool exportRes = db.exportSpatiaLite( QStringList("amenity") << "name" << "highway" );
176+
if ( !db.errorString().isEmpty() )
177+
qDebug( "EXPORT-1 ERR: %s", db.errorString().toAscii().data() );
178+
QCOMPARE( exportRes1, true );
179+
180+
181+
bool exportRes2 = db.exportSpatiaLite( QgsOSMDatabase::Polyline, "sl_lines", QStringList( "building" ) );
182+
//bool exportRes2 = db.exportSpatiaLite( QStringList("amenity") << "name" << "highway" );
183+
if ( !db.errorString().isEmpty() )
184+
qDebug( "EXPORT-2 ERR: %s", db.errorString().toAscii().data() );
185+
QCOMPARE( exportRes2, true );
186+
187+
188+
// TODO: test exported data
189+
}
190+
191+
192+
QTEST_MAIN( TestOpenStreetMap )
193+
194+
#include "moc_testopenstreetmap.cxx"
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<osm version="0.6" generator="CGImap 0.0.2" copyright="OpenStreetMap and contributors" attribution="http://www.openstreetmap.org/copyright" license="http://opendatacommons.org/licenses/odbl/1-0/">
3+
4+
<bounds minlat="50.0607644" minlon="14.4217296" maxlat="50.0705202" maxlon="14.4366398"/>
5+
6+
<!-- node -->
7+
<node id="11111" lat="50.0651387" lon="14.4277148" user="Radomír Černoch" uid="51295" visible="true" version="2" changeset="1984279" timestamp="2009-07-30T13:22:24Z">
8+
<tag k="addr:conscriptionnumber" v="455"/>
9+
<tag k="addr:housenumber" v="455/23"/>
10+
<tag k="addr:postcode" v="12800"/>
11+
<tag k="addr:street" v="Jaromírova"/>
12+
<tag k="addr:streetnumber" v="23"/>
13+
<tag k="source:addr" v="uir_adr"/>
14+
<tag k="uir_adr:ADRESA_KOD" v="21738092"/>
15+
</node>
16+
17+
<!-- closed way -->
18+
<node id="360769661" lat="50.0665514" lon="14.4270245" user="BiIbo" uid="3516" visible="true" version="1" changeset="819377" timestamp="2009-03-15T16:58:35Z"/>
19+
<node id="360769664" lat="50.0665121" lon="14.4270254" user="BiIbo" uid="3516" visible="true" version="1" changeset="819377" timestamp="2009-03-15T16:58:36Z"/>
20+
<node id="360769666" lat="50.0665127" lon="14.4270765" user="BiIbo" uid="3516" visible="true" version="1" changeset="819377" timestamp="2009-03-15T16:58:36Z"/>
21+
<node id="360769670" lat="50.0665514" lon="14.4270765" user="BiIbo" uid="3516" visible="true" version="1" changeset="819377" timestamp="2009-03-15T16:58:36Z"/>
22+
<way id="32137532" user="BiIbo" uid="3516" visible="true" version="1" changeset="819377" timestamp="2009-03-15T16:58:37Z">
23+
<nd ref="360769661"/>
24+
<nd ref="360769664"/>
25+
<nd ref="360769666"/>
26+
<nd ref="360769670"/>
27+
<nd ref="360769661"/>
28+
<tag k="building" v="yes"/>
29+
<tag k="layer" v="1"/>
30+
<tag k="source" v="cuzk:km"/>
31+
</way>
32+
33+
<!-- relation -->
34+
<relation id="126753" user="BiIbo" uid="3516" visible="true" version="1" changeset="1078773" timestamp="2009-05-04T19:51:58Z">
35+
<member type="way" ref="32137532" role="outer"/>
36+
<!-- todo <member type="way" ref="34075795" role="inner"/> -->
37+
<!--<member type="way" ref="34075796" role="inner"/>-->
38+
<tag k="type" v="multipolygon"/>
39+
</relation>
40+
41+
</osm>

0 commit comments

Comments
 (0)
Please sign in to comment.