From 954619a7aff6fe289aeff6a9adbc4e8a01795088 Mon Sep 17 00:00:00 2001 From: Transporter Date: Wed, 5 Jun 2024 00:28:28 +0200 Subject: [PATCH] Example for reading shapefiles with GDAL and displaying the geometry. (#53) Example for reading shapefiles with GDAL and displaying the geometry. --------- Co-authored-by: AmonRaNet --- CMakeLists.txt | 4 + samples/gdal-shapefile/CMakeLists.txt | 40 +++++++ samples/gdal-shapefile/main.cpp | 37 ++++++ samples/gdal-shapefile/mainwindow.cpp | 121 +++++++++++++++++++ samples/gdal-shapefile/mainwindow.h | 35 ++++++ samples/gdal-shapefile/polygon.cpp | 163 ++++++++++++++++++++++++++ samples/gdal-shapefile/polygon.h | 56 +++++++++ 7 files changed, 456 insertions(+) create mode 100644 samples/gdal-shapefile/CMakeLists.txt create mode 100644 samples/gdal-shapefile/main.cpp create mode 100644 samples/gdal-shapefile/mainwindow.cpp create mode 100644 samples/gdal-shapefile/mainwindow.h create mode 100644 samples/gdal-shapefile/polygon.cpp create mode 100644 samples/gdal-shapefile/polygon.h diff --git a/CMakeLists.txt b/CMakeLists.txt index dcf99eb..b00264b 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ project(QGeoView LANGUAGES C CXX) option(BUILD_EXAMPLES "Build examples" ON) +find_package(GDAL CONFIG) add_subdirectory(lib) if (${BUILD_EXAMPLES}) message(STATUS "Enabled building of examples") @@ -23,6 +24,9 @@ if (${BUILD_EXAMPLES}) add_subdirectory(samples/mouse-actions) add_subdirectory(samples/camera-actions) add_subdirectory(samples/drag-and-drop) + if(GDAL_FOUND) + add_subdirectory(samples/gdal-shapefile) + endif() else () message(STATUS "Disabled building of examples") endif () diff --git a/samples/gdal-shapefile/CMakeLists.txt b/samples/gdal-shapefile/CMakeLists.txt new file mode 100644 index 0000000..df1ed24 --- /dev/null +++ b/samples/gdal-shapefile/CMakeLists.txt @@ -0,0 +1,40 @@ +set(CMAKE_CXX_STANDARD 11) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +# Set the QT version +find_package(Qt6 COMPONENTS Core QUIET) +if (NOT Qt6_FOUND) + set(QT_VERSION 5 CACHE STRING "Qt version for QGeoView") +else() + set(QT_VERSION 6 CACHE STRING "Qt version for QGeoView") +endif() + +find_package(Qt${QT_VERSION} REQUIRED COMPONENTS + Core + Gui + Widgets + Network +) + +add_executable(qgeoview-samples-gdal-shapefile + main.cpp + mainwindow.h + mainwindow.cpp + polygon.cpp + polygon.h +) + +target_link_libraries(qgeoview-samples-gdal-shapefile + PRIVATE + Qt${QT_VERSION}::Core + Qt${QT_VERSION}::Network + Qt${QT_VERSION}::Gui + Qt${QT_VERSION}::Widgets + QGeoView + qgeoview-samples-shared + GDAL::GDAL +) diff --git a/samples/gdal-shapefile/main.cpp b/samples/gdal-shapefile/main.cpp new file mode 100644 index 0000000..6c72f24 --- /dev/null +++ b/samples/gdal-shapefile/main.cpp @@ -0,0 +1,37 @@ +/*************************************************************************** + * QGeoView is a Qt / C ++ widget for visualizing geographic data. + * Copyright (C) 2018-2023 Andrey Yaroshenko. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 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 Lesser General Public License + * along with this program; if not, see https://www.gnu.org/licenses. + ****************************************************************************/ + +#include +#include + +#include "mainwindow.h" + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + app.setApplicationName("QGeoView Samples"); + + QCommandLineParser parser; + parser.addHelpOption(); + parser.addVersionOption(); + parser.process(app); + + MainWindow window; + window.show(); + return app.exec(); +} diff --git a/samples/gdal-shapefile/mainwindow.cpp b/samples/gdal-shapefile/mainwindow.cpp new file mode 100644 index 0000000..19f7aef --- /dev/null +++ b/samples/gdal-shapefile/mainwindow.cpp @@ -0,0 +1,121 @@ +/*************************************************************************** + * QGeoView is a Qt / C ++ widget for visualizing geographic data. + * Copyright (C) 2018-2023 Andrey Yaroshenko. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 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 Lesser General Public License + * along with this program; if not, see https://www.gnu.org/licenses. + ****************************************************************************/ + +#include "mainwindow.h" + +#include +#include +#include + +#include +#include +#include "polygon.h" + +#include "cpl_conv.h" +#include "ogrsf_frmts.h" + +QList convert(OGRPolygon* poPolygon) +{ + OGRPoint ptTemp; + QList result; + OGRLinearRing* poExteriorRing = poPolygon->getExteriorRing(); + int NumberOfExteriorRingVertices = poExteriorRing->getNumPoints(); + for (int k = 0; k < NumberOfExteriorRingVertices; k++) { + poExteriorRing->getPoint(k, &ptTemp); + result << QGV::GeoPos(ptTemp.getY(), ptTemp.getX()); + } + return result; +} + +MainWindow::MainWindow() +{ + setWindowTitle("QGeoView Samples - Shapefile"); + + mMap = new QGVMap(this); + setCentralWidget(mMap); + + Helpers::setupCachedNetworkAccessManager(this); + + // Background layer + auto osmLayer = new QGVLayerOSM(); + mMap->addItem(osmLayer); + + // Please adapt the following two points to your system: + // 1. GDAL data path + QDir gdalData(QApplication::applicationDirPath()); + gdalData.cd("gdal"); + // 2. PROJ data path + QDir projData(gdalData); + + // GDAL configuration (https://gdal.org/user/configoptions.html) + std::string GDAL_DATA = QDir::toNativeSeparators(gdalData.absolutePath()).toStdString(); + CPLSetConfigOption("GDAL_DATA", GDAL_DATA.c_str()); + std::string PROJ_DATA = QDir::toNativeSeparators(projData.absolutePath()).toStdString(); + CPLSetConfigOption("PROJ_DATA", PROJ_DATA.c_str()); + + GDALAllRegister(); // Load GDAL drivers + GDALDataset *poDS = static_cast(GDALOpenEx("countries.shp", GDAL_OF_VECTOR, NULL, NULL, NULL)); // Open vector file + if (poDS == NULL) { + printf("Open failed.\n"); + exit(1); + } + + for (int iLayer = 0; iLayer < poDS->GetLayerCount(); iLayer++) + { + OGRLayer* poLayer = poDS->GetLayer(iLayer); + OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn(); + poLayer->ResetReading(); + OGRFeature* poFeature; + while ((poFeature = poLayer->GetNextFeature()) != NULL) { + OGRGeometry* poGeometry = poFeature->GetGeometryRef(); + auto type = wkbFlatten(poGeometry->getGeometryType()); + if (poGeometry != NULL && wkbFlatten(poGeometry->getGeometryType()) == wkbPolygon) { + OGRPolygon* poPolygon = (OGRPolygon*)poGeometry; + if (poPolygon->IsValid()) { + QList points = convert(poPolygon); + if (points.count() > 2) + mMap->addItem(new Polygon(points, Qt::red, Qt::blue)); + } + } else if (poGeometry != NULL && wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPolygon) { + OGRMultiPolygon* poMultiPolygon = (OGRMultiPolygon*)poGeometry; + for (int i = 0; i < poMultiPolygon->getNumGeometries(); i++) { + OGRPolygon* poPolygon = (OGRPolygon*)poMultiPolygon->getGeometryRef(i); + if (poPolygon->IsValid()) { + QList points = convert(poPolygon); + if (points.count() > 2) + mMap->addItem(new Polygon(points, Qt::red, Qt::blue)); + } + } + } else { + printf("no polygon geometry\n"); + } + OGRFeature::DestroyFeature(poFeature); + } + } + GDALClose(poDS); + + // Show whole world + QTimer::singleShot(100, this, [this]() { + auto target = mMap->getProjection()->boundaryGeoRect(); + mMap->cameraTo(QGVCameraActions(mMap).scaleTo(target)); + }); +} + +MainWindow::~MainWindow() +{ +} diff --git a/samples/gdal-shapefile/mainwindow.h b/samples/gdal-shapefile/mainwindow.h new file mode 100644 index 0000000..2f74bdd --- /dev/null +++ b/samples/gdal-shapefile/mainwindow.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * QGeoView is a Qt / C ++ widget for visualizing geographic data. + * Copyright (C) 2018-2023 Andrey Yaroshenko. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 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 Lesser General Public License + * along with this program; if not, see https://www.gnu.org/licenses. + ****************************************************************************/ + +#pragma once + +#include + +#include + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(); + ~MainWindow(); + +private: + QGVMap* mMap; +}; diff --git a/samples/gdal-shapefile/polygon.cpp b/samples/gdal-shapefile/polygon.cpp new file mode 100644 index 0000000..024b79a --- /dev/null +++ b/samples/gdal-shapefile/polygon.cpp @@ -0,0 +1,163 @@ +/*************************************************************************** + * QGeoView is a Qt / C ++ widget for visualizing geographic data. + * Copyright (C) 2018-2023 Andrey Yaroshenko. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 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 Lesser General Public License + * along with this program; if not, see https://www.gnu.org/licenses. + ****************************************************************************/ + +#include "polygon.h" + +#include + +Polygon::Polygon(const PointList& geoPoints, QColor stroke, QColor fill) + : mGeoPoints(geoPoints) + , mColorStroke(stroke) + , mColorFill(fill) +{ +} + +void Polygon::setPoints(const PointList& geoPoints) +{ + mGeoPoints = geoPoints; + + // Geo coordinates need to be converted manually again to projection + onProjection(getMap()); + + // Now we can inform QGV about changes for this + resetBoundary(); + refresh(); +} + +PointList Polygon::getPoints() const +{ + return mGeoPoints; +} + +void Polygon::onProjection(QGVMap* geoMap) +{ + QGVDrawItem::onProjection(geoMap); + mProjPoints.clear(); + for (const QGV::GeoPos& pos : mGeoPoints) + mProjPoints << geoMap->getProjection()->geoToProj(pos); +} + +QPainterPath Polygon::projShape() const +{ + QPainterPath path; + path.addPolygon(mProjPoints); + return path; +} + +void Polygon::projPaint(QPainter* painter) +{ + QPen pen = QPen(QBrush(mColorStroke), 1); + pen.setCosmetic(true); + painter->setPen(pen); + painter->setBrush(QBrush(mColorFill)); + painter->drawPolygon(mProjPoints); +} + +QPointF Polygon::projAnchor() const +{ + return mProjPoints.boundingRect().center(); +} + +QTransform Polygon::projTransform() const +{ + // This method is optional (needed flag is QGV::ItemFlag::Transformed). + // Custom transformation for item. + // In this case we rotate item by 45 degree. + + return QGV::createTransfromAzimuth(projAnchor(), 45); +} + +QString Polygon::projTooltip(const QPointF& projPos) const +{ + // This method is optional (when empty return then no tooltip). + // Text for mouse tool tip. + + auto geo = getMap()->getProjection()->projToGeo(projPos); + + return "Polygon with color " + mColorFill.name() + "\nPosition " + geo.latToString() + " " + geo.lonToString(); +} + +void Polygon::projOnMouseClick(const QPointF& projPos) +{ + // This method is optional (needed flag is QGV::ItemFlag::Clickable). + // Custom reaction to item single mouse click. + // To avoid collision with item selection this code applies only if item selection disabled. + // In this case we change opacity for item. + + if (!isSelectable()) { + if (getOpacity() <= 0.5) + setOpacity(1.0); + else + setOpacity(0.5); + + qInfo() << "single click" << projPos; + } else { + setOpacity(1.0); + } +} + +void Polygon::projOnMouseDoubleClick(const QPointF& projPos) +{ + // This method is optional (needed flag is QGV::ItemFlag::Clickable). + // Custom reaction to item double mouse click. + // In this case we change color for item. + + const QList colors = { Qt::red, Qt::blue, Qt::green, Qt::gray, Qt::cyan, Qt::magenta, Qt::yellow }; + + const auto iter = + std::find_if(colors.begin(), colors.end(), [this](const QColor& color) { return color == mColorFill; }); + mColorFill = colors[(iter - colors.begin() + 1) % colors.size()]; + repaint(); + + setOpacity(1.0); + + qInfo() << "double click" << projPos; +} + +void Polygon::projOnObjectStartMove(const QPointF& projPos) +{ + // This method is optional (needed flag is QGV::ItemFlag::Movable). + // Custom reaction to item move start. + // In this case we only log message. + + qInfo() << "object move started at" << projPos; +} + +void Polygon::projOnObjectMovePos(const QPointF& projPos) +{ + // This method is optional (needed flag is QGV::ItemFlag::Movable). + // Custom reaction to mouse pos change when item move is started. + // In this case actually changing location of object. + + PointList newPoints; + for (const QPointF& pt : mProjPoints) + newPoints << getMap()->getProjection()->projToGeo(pt + projPos); + + setPoints(newPoints); + + qInfo() << "object moved" << mProjPoints; +} + +void Polygon::projOnObjectStopMove(const QPointF& projPos) +{ + // This method is optional (needed flag is QGV::ItemFlag::Movable). + // Custom reaction to item move finished. + // In this case we only log message. + + qInfo() << "object move stopped" << projPos; +} diff --git a/samples/gdal-shapefile/polygon.h b/samples/gdal-shapefile/polygon.h new file mode 100644 index 0000000..2dc9862 --- /dev/null +++ b/samples/gdal-shapefile/polygon.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * QGeoView is a Qt / C ++ widget for visualizing geographic data. + * Copyright (C) 2018-2023 Andrey Yaroshenko. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 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 Lesser General Public License + * along with this program; if not, see https://www.gnu.org/licenses. + ****************************************************************************/ + +#pragma once + +#include + +#include +#include + +typedef QList PointList; + +class Polygon : public QGVDrawItem +{ + Q_OBJECT + +public: + explicit Polygon(const PointList& geoPoints, QColor stroke, QColor fill); + + void setPoints(const PointList& geoPoints); + PointList getPoints() const; + +private: + void onProjection(QGVMap* geoMap) override; + QPainterPath projShape() const override; + void projPaint(QPainter* painter) override; + QPointF projAnchor() const override; + QTransform projTransform() const override; + QString projTooltip(const QPointF& projPos) const override; + void projOnMouseClick(const QPointF& projPos) override; + void projOnMouseDoubleClick(const QPointF& projPos) override; + void projOnObjectStartMove(const QPointF& projPos) override; + void projOnObjectMovePos(const QPointF& projPos) override; + void projOnObjectStopMove(const QPointF& projPos) override; + +private: + PointList mGeoPoints; + QPolygonF mProjPoints; + QColor mColorStroke; + QColor mColorFill; +};