Draw a triangle on the map

This commit is contained in:
Dan Paulat 2021-06-26 21:35:25 -05:00
parent b1e00cca83
commit 79ab14ab95
6 changed files with 250 additions and 364 deletions

View file

@ -1,2 +1,8 @@
cmake_minimum_required(VERSION 3.11) cmake_minimum_required(VERSION 3.11)
set_property(DIRECTORY
APPEND
PROPERTY CMAKE_CONFIGURE_DEPENDS
scwx-qt.cmake)
include(scwx-qt.cmake) include(scwx-qt.cmake)

View file

@ -11,6 +11,8 @@ set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Boost)
# QtCreator supports the following variables for Android, which are identical to qmake Android variables. # QtCreator supports the following variables for Android, which are identical to qmake Android variables.
# Check https://doc.qt.io/qt/deployment-android.html for more information. # Check https://doc.qt.io/qt/deployment-android.html for more information.
# They need to be set before the find_package( ...) calls below. # They need to be set before the find_package( ...) calls below.
@ -47,8 +49,10 @@ set(HDR_MAIN source/scwx/qt/main/main_window.hpp)
set(SRC_MAIN source/scwx/qt/main/main.cpp set(SRC_MAIN source/scwx/qt/main/main.cpp
source/scwx/qt/main/main_window.cpp) source/scwx/qt/main/main_window.cpp)
set(UI_MAIN source/scwx/qt/main/main_window.ui) set(UI_MAIN source/scwx/qt/main/main_window.ui)
set(HDR_MAP source/scwx/qt/map/map_widget.hpp) set(HDR_MAP source/scwx/qt/map/map_widget.hpp
set(SRC_MAP source/scwx/qt/map/map_widget.cpp) source/scwx/qt/map/triangle_layer.hpp)
set(SRC_MAP source/scwx/qt/map/map_widget.cpp
source/scwx/qt/map/triangle_layer.cpp)
set(TS_FILES ts/scwx_en_US.ts) set(TS_FILES ts/scwx_en_US.ts)
@ -76,4 +80,6 @@ target_include_directories(scwx-qt PRIVATE ${scwx-qt_SOURCE_DIR}/source)
target_link_libraries(scwx-qt PRIVATE Qt${QT_VERSION_MAJOR}::Widgets target_link_libraries(scwx-qt PRIVATE Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::OpenGLWidgets Qt${QT_VERSION_MAJOR}::OpenGLWidgets
qmapboxgl) Boost::log
qmapboxgl
opengl32)

View file

@ -1,5 +1,7 @@
#include "map_widget.hpp" #include "map_widget.hpp"
#include <scwx/qt/map/triangle_layer.hpp>
#include <QApplication> #include <QApplication>
#include <QColor> #include <QColor>
#include <QDebug> #include <QDebug>
@ -44,8 +46,26 @@ void MapWidget::changeStyle()
{ {
currentStyleIndex = 0; currentStyleIndex = 0;
} }
}
void MapWidget::AddLayers()
{
// QMapboxGL::addCustomLayer will take ownership of the QScopedPointer
QScopedPointer<QMapbox::CustomLayerHostInterface> pHost(new TriangleLayer());
sourceAdded_ = false; QString before = "ferry";
for (const QString& layer : map_->layerIds())
{
// Draw below tunnels, ferries and roads
if (layer.startsWith("tunnel") || layer.startsWith("ferry") ||
layer.startsWith("road"))
{
before = layer;
break;
}
}
map_->addCustomLayer("triangle", pHost, before);
} }
void MapWidget::keyPressEvent(QKeyEvent* ev) void MapWidget::keyPressEvent(QKeyEvent* ev)
@ -55,362 +75,9 @@ void MapWidget::keyPressEvent(QKeyEvent* ev)
case Qt::Key_S: changeStyle(); break; case Qt::Key_S: changeStyle(); break;
case Qt::Key_L: case Qt::Key_L:
{ {
if (sourceAdded_) for (const QString& layer : map_->layerIds())
{ {
return; qDebug() << "Layer: " << layer;
}
sourceAdded_ = true;
// Not in all styles, but will work on streets
QString before = "waterway-label";
QFile geojson(":source1.geojson");
geojson.open(QIODevice::ReadOnly);
// The data source for the route line and markers
QVariantMap routeSource;
routeSource["type"] = "geojson";
routeSource["data"] = geojson.readAll();
map_->addSource("routeSource", routeSource);
// The route case, painted before the route
QVariantMap routeCase;
routeCase["id"] = "routeCase";
routeCase["type"] = "line";
routeCase["source"] = "routeSource";
map_->addLayer(routeCase, before);
map_->setPaintProperty("routeCase", "line-color", QColor("white"));
map_->setPaintProperty("routeCase", "line-width", 20.0);
map_->setLayoutProperty("routeCase", "line-join", "round");
map_->setLayoutProperty("routeCase", "line-cap", "round");
// The route, painted on top of the route case
QVariantMap route;
route["id"] = "route";
route["type"] = "line";
route["source"] = "routeSource";
map_->addLayer(route, before);
map_->setPaintProperty("route", "line-color", QColor("blue"));
map_->setPaintProperty("route", "line-width", 8.0);
map_->setLayoutProperty("route", "line-join", "round");
map_->setLayoutProperty("route", "line-cap", "round");
QVariantList lineDashArray;
lineDashArray.append(1);
lineDashArray.append(2);
map_->setPaintProperty("route", "line-dasharray", lineDashArray);
// Markers at the beginning and end of the route
map_->addImage("label-arrow", QImage(":label-arrow.svg"));
map_->addImage("label-background", QImage(":label-background.svg"));
QVariantMap markerArrow;
markerArrow["id"] = "markerArrow";
markerArrow["type"] = "symbol";
markerArrow["source"] = "routeSource";
map_->addLayer(markerArrow);
map_->setLayoutProperty("markerArrow", "icon-image", "label-arrow");
map_->setLayoutProperty("markerArrow", "icon-size", 0.5);
map_->setLayoutProperty("markerArrow", "icon-ignore-placement", true);
QVariantList arrowOffset;
arrowOffset.append(0.0);
arrowOffset.append(-15.0);
map_->setLayoutProperty("markerArrow", "icon-offset", arrowOffset);
QVariantMap markerBackground;
markerBackground["id"] = "markerBackground";
markerBackground["type"] = "symbol";
markerBackground["source"] = "routeSource";
map_->addLayer(markerBackground);
map_->setLayoutProperty(
"markerBackground", "icon-image", "label-background");
map_->setLayoutProperty("markerBackground", "text-field", "{name}");
map_->setLayoutProperty("markerBackground", "icon-text-fit", "both");
map_->setLayoutProperty(
"markerBackground", "icon-ignore-placement", true);
map_->setLayoutProperty(
"markerBackground", "text-ignore-placement", true);
map_->setLayoutProperty("markerBackground", "text-anchor", "left");
map_->setLayoutProperty("markerBackground", "text-size", 16.0);
map_->setLayoutProperty("markerBackground", "text-padding", 0.0);
map_->setLayoutProperty("markerBackground", "text-line-height", 1.0);
map_->setLayoutProperty("markerBackground", "text-max-width", 8.0);
QVariantList iconTextFitPadding;
iconTextFitPadding.append(15.0);
iconTextFitPadding.append(10.0);
iconTextFitPadding.append(15.0);
iconTextFitPadding.append(10.0);
map_->setLayoutProperty(
"markerBackground", "icon-text-fit-padding", iconTextFitPadding);
QVariantList backgroundOffset;
backgroundOffset.append(-0.5);
backgroundOffset.append(-1.5);
map_->setLayoutProperty(
"markerBackground", "text-offset", backgroundOffset);
map_->setPaintProperty("markerBackground", "text-color", QColor("white"));
QVariantList filterExpression;
filterExpression.append("==");
filterExpression.append("$type");
filterExpression.append("Point");
QVariantList filter;
filter.append(filterExpression);
map_->setFilter("markerArrow", filter);
map_->setFilter("markerBackground", filter);
// Tilt the labels when tilting the map and make them larger
map_->setLayoutProperty("road-label-large", "text-size", 30.0);
map_->setLayoutProperty(
"road-label-large", "text-pitch-alignment", "viewport");
map_->setLayoutProperty("road-label-medium", "text-size", 30.0);
map_->setLayoutProperty(
"road-label-medium", "text-pitch-alignment", "viewport");
map_->setLayoutProperty(
"road-label-small", "text-pitch-alignment", "viewport");
map_->setLayoutProperty("road-label-small", "text-size", 30.0);
// Buildings extrusion
QVariantMap buildings;
buildings["id"] = "3d-buildings";
buildings["source"] = "composite";
buildings["source-layer"] = "building";
buildings["type"] = "fill-extrusion";
buildings["minzoom"] = 15.0;
map_->addLayer(buildings);
QVariantList buildingsFilterExpression;
buildingsFilterExpression.append("==");
buildingsFilterExpression.append("extrude");
buildingsFilterExpression.append("true");
QVariantList buildingsFilter;
buildingsFilter.append(buildingsFilterExpression);
map_->setFilter("3d-buildings", buildingsFilterExpression);
QString fillExtrusionColorJSON = R"JSON(
[
"interpolate",
["linear"],
["get", "height"],
0.0, "blue",
20.0, "royalblue",
40.0, "cyan",
60.0, "lime",
80.0, "yellow",
100.0, "red"
]
)JSON";
map_->setPaintProperty(
"3d-buildings", "fill-extrusion-color", fillExtrusionColorJSON);
map_->setPaintProperty("3d-buildings", "fill-extrusion-opacity", .6);
QVariantMap extrusionHeight;
extrusionHeight["type"] = "identity";
extrusionHeight["property"] = "height";
map_->setPaintProperty(
"3d-buildings", "fill-extrusion-height", extrusionHeight);
QVariantMap extrusionBase;
extrusionBase["type"] = "identity";
extrusionBase["property"] = "min_height";
map_->setPaintProperty(
"3d-buildings", "fill-extrusion-base", extrusionBase);
}
break;
case Qt::Key_1:
{
if (symbolAnnotationId_.isNull())
{
QMapbox::Coordinate coordinate = map_->coordinate();
QMapbox::SymbolAnnotation symbol {coordinate, "default_marker"};
map_->addAnnotationIcon("default_marker",
QImage(":default_marker.svg"));
symbolAnnotationId_ = map_->addAnnotation(
QVariant::fromValue<QMapbox::SymbolAnnotation>(symbol));
}
else
{
map_->removeAnnotation(symbolAnnotationId_.toUInt());
symbolAnnotationId_.clear();
}
}
break;
case Qt::Key_2:
{
if (lineAnnotationId_.isNull())
{
QMapbox::Coordinates coordinates;
coordinates.push_back(map_->coordinateForPixel({0, 0}));
coordinates.push_back(map_->coordinateForPixel(
{qreal(size().width()), qreal(size().height())}));
QMapbox::CoordinatesCollection collection;
collection.push_back(coordinates);
QMapbox::CoordinatesCollections lineGeometry;
lineGeometry.push_back(collection);
QMapbox::ShapeAnnotationGeometry annotationGeometry(
QMapbox::ShapeAnnotationGeometry::LineStringType, lineGeometry);
QMapbox::LineAnnotation line;
line.geometry = annotationGeometry;
line.opacity = 0.5f;
line.width = 1.0f;
line.color = Qt::red;
lineAnnotationId_ = map_->addAnnotation(
QVariant::fromValue<QMapbox::LineAnnotation>(line));
}
else
{
map_->removeAnnotation(lineAnnotationId_.toUInt());
lineAnnotationId_.clear();
}
}
break;
case Qt::Key_3:
{
if (fillAnnotationId_.isNull())
{
QMapbox::Coordinates coordinates;
coordinates.push_back(
map_->coordinateForPixel({qreal(size().width()), 0}));
coordinates.push_back(map_->coordinateForPixel(
{qreal(size().width()), qreal(size().height())}));
coordinates.push_back(
map_->coordinateForPixel({0, qreal(size().height())}));
coordinates.push_back(map_->coordinateForPixel({0, 0}));
QMapbox::CoordinatesCollection collection;
collection.push_back(coordinates);
QMapbox::CoordinatesCollections fillGeometry;
fillGeometry.push_back(collection);
QMapbox::ShapeAnnotationGeometry annotationGeometry(
QMapbox::ShapeAnnotationGeometry::PolygonType, fillGeometry);
QMapbox::FillAnnotation fill;
fill.geometry = annotationGeometry;
fill.opacity = 0.5f;
fill.color = Qt::green;
fill.outlineColor = QVariant::fromValue<QColor>(QColor(Qt::black));
fillAnnotationId_ = map_->addAnnotation(
QVariant::fromValue<QMapbox::FillAnnotation>(fill));
}
else
{
map_->removeAnnotation(fillAnnotationId_.toUInt());
fillAnnotationId_.clear();
}
}
break;
case Qt::Key_5:
{
if (map_->layerExists("circleLayer"))
{
map_->removeLayer("circleLayer");
map_->removeSource("circleSource");
}
else
{
QMapbox::Coordinates coordinates;
coordinates.push_back(map_->coordinate());
QMapbox::CoordinatesCollection collection;
collection.push_back(coordinates);
QMapbox::CoordinatesCollections point;
point.push_back(collection);
QMapbox::Feature feature(QMapbox::Feature::PointType, point, {}, {});
QVariantMap circleSource;
circleSource["type"] = "geojson";
circleSource["data"] = QVariant::fromValue<QMapbox::Feature>(feature);
map_->addSource("circleSource", circleSource);
QVariantMap circle;
circle["id"] = "circleLayer";
circle["type"] = "circle";
circle["source"] = "circleSource";
map_->addLayer(circle);
map_->setPaintProperty("circleLayer", "circle-radius", 10.0);
map_->setPaintProperty("circleLayer", "circle-color", QColor("black"));
}
}
break;
case Qt::Key_6:
{
if (map_->layerExists("innerCirclesLayer") ||
map_->layerExists("outerCirclesLayer"))
{
map_->removeLayer("innerCirclesLayer");
map_->removeLayer("outerCirclesLayer");
map_->removeSource("innerCirclesSource");
map_->removeSource("outerCirclesSource");
}
else
{
auto makePoint = [&](double dx, double dy, const QString& color) {
auto coordinate = map_->coordinate();
coordinate.first += dx;
coordinate.second += dy;
return QMapbox::Feature {QMapbox::Feature::PointType,
{{{coordinate}}},
{{"color", color}},
{}};
};
// multiple features by QVector<QMapbox::Feature>
QVector<QMapbox::Feature> inner {makePoint(0.001, 0, "red"),
makePoint(0, 0.001, "green"),
makePoint(0, -0.001, "blue")};
map_->addSource(
"innerCirclesSource",
{{"type", "geojson"}, {"data", QVariant::fromValue(inner)}});
map_->addLayer({{"id", "innerCirclesLayer"},
{"type", "circle"},
{"source", "innerCirclesSource"}});
// multiple features by QList<QMapbox::Feature>
QList<QMapbox::Feature> outer {makePoint(0.002, 0.002, "cyan"),
makePoint(-0.002, 0.002, "magenta"),
makePoint(0.002, -0.002, "yellow"),
makePoint(-0.002, -0.002, "black")};
map_->addSource(
"outerCirclesSource",
{{"type", "geojson"}, {"data", QVariant::fromValue(outer)}});
map_->addLayer({{"id", "outerCirclesLayer"},
{"type", "circle"},
{"source", "outerCirclesSource"}});
QVariantList getColor {"get", "color"};
map_->setPaintProperty("innerCirclesLayer", "circle-radius", 10.0);
map_->setPaintProperty("innerCirclesLayer", "circle-color", getColor);
map_->setPaintProperty("outerCirclesLayer", "circle-radius", 15.0);
map_->setPaintProperty("outerCirclesLayer", "circle-color", getColor);
} }
} }
break; break;
@ -512,6 +179,8 @@ void MapWidget::initializeGL()
map_->setStyleUrl(styleUrl); map_->setStyleUrl(styleUrl);
setWindowTitle(QString("Mapbox GL: ") + styleUrl); setWindowTitle(QString("Mapbox GL: ") + styleUrl);
} }
connect(map_.get(), &QMapboxGL::mapChanged, this, &MapWidget::mapChanged);
} }
void MapWidget::paintGL() void MapWidget::paintGL()
@ -523,5 +192,13 @@ void MapWidget::paintGL()
map_->render(); map_->render();
} }
void MapWidget::mapChanged(QMapboxGL::MapChange mapChange)
{
if (mapChange == QMapboxGL::MapChangeDidFinishLoadingStyle)
{
AddLayers();
}
}
} // namespace qt } // namespace qt
} // namespace scwx } // namespace scwx

View file

@ -38,6 +38,8 @@ private:
void initializeGL() override final; void initializeGL() override final;
void paintGL() override final; void paintGL() override final;
void AddLayers();
QPointF lastPos_; QPointF lastPos_;
QMapboxGLSettings settings_; QMapboxGLSettings settings_;
@ -45,11 +47,8 @@ private:
uint64_t frameDraws_ = 0; uint64_t frameDraws_ = 0;
QVariant symbolAnnotationId_; private slots:
QVariant lineAnnotationId_; void mapChanged(QMapboxGL::MapChange);
QVariant fillAnnotationId_;
bool sourceAdded_ = false;
}; };
} // namespace qt } // namespace qt

View file

@ -0,0 +1,167 @@
#include <scwx/qt/map/triangle_layer.hpp>
#include <QOpenGLFunctions_3_3_Core>
#include <boost/log/trivial.hpp>
namespace scwx
{
namespace qt
{
static const std::string logPrefix_ = "[scwx::map::triangle_layer] ";
class TriangleLayerImpl
{
public:
explicit TriangleLayerImpl() :
gl_(),
shaderProgram_ {GL_INVALID_INDEX},
vbo_ {GL_INVALID_INDEX},
vao_ {GL_INVALID_INDEX}
{
gl_.initializeOpenGLFunctions();
}
~TriangleLayerImpl() = default;
QOpenGLFunctions_3_3_Core gl_;
GLuint shaderProgram_;
GLuint vbo_;
GLuint vao_;
};
TriangleLayer::TriangleLayer() : p(std::make_unique<TriangleLayerImpl>()) {}
TriangleLayer::~TriangleLayer() = default;
TriangleLayer::TriangleLayer(TriangleLayer&&) noexcept = default;
TriangleLayer& TriangleLayer::operator=(TriangleLayer&&) noexcept = default;
void TriangleLayer::initialize()
{
QOpenGLFunctions_3_3_Core& gl = p->gl_;
static const char* vertexShaderSource =
"#version 330 core\n"
"layout (location = 0) in vec3 aPos;"
"void main()"
"{"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);"
"}";
static const char* fragmentShaderSource =
"#version 330 core\n"
"out vec4 FragColor;"
"void main()"
"{"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);"
"}";
BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "initialize()";
GLint success;
// Create a vertex shader
GLuint vertexShader = gl.glCreateShader(GL_VERTEX_SHADER);
// Attach the shader source code and compile the shader
gl.glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
gl.glCompileShader(vertexShader);
// Check for errors
gl.glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
char infoLog[512];
gl.glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
BOOST_LOG_TRIVIAL(error)
<< logPrefix_ << "Vertex shader compilation failed";
}
// Create a fragment shader
GLuint fragmentShader = gl.glCreateShader(GL_FRAGMENT_SHADER);
// Attach the shader source and compile the shader
gl.glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
gl.glCompileShader(fragmentShader);
// Check for errors
gl.glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
char infoLog[512];
gl.glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
BOOST_LOG_TRIVIAL(error)
<< logPrefix_ << "Fragment shader compilation failed: " << infoLog;
}
// Create shader program
p->shaderProgram_ = gl.glCreateProgram();
gl.glAttachShader(p->shaderProgram_, vertexShader);
gl.glAttachShader(p->shaderProgram_, fragmentShader);
gl.glLinkProgram(p->shaderProgram_);
// Check for errors
gl.glGetProgramiv(p->shaderProgram_, GL_LINK_STATUS, &success);
if (!success)
{
char infoLog[512];
gl.glGetProgramInfoLog(p->shaderProgram_, 512, NULL, infoLog);
BOOST_LOG_TRIVIAL(error)
<< logPrefix_ << "Shader program link failed: " << infoLog;
}
// Delete shaders
gl.glDeleteShader(vertexShader);
gl.glDeleteShader(fragmentShader);
// Define 3 (x,y,z) vertices
GLfloat vertices[] = {
-0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f};
// Generate a vertex buffer object
gl.glGenBuffers(1, &p->vbo_);
// Generate a vertex array object
gl.glGenVertexArrays(1, &p->vao_);
// Bind vertex array object
gl.glBindVertexArray(p->vao_);
// Copy vertices array in a buffer for OpenGL to use
gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_);
gl.glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// Set the vertex attributes pointers
gl.glVertexAttribPointer(
0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), static_cast<void*>(0));
gl.glEnableVertexAttribArray(0);
}
void TriangleLayer::render(const QMapbox::CustomLayerRenderParameters&)
{
QOpenGLFunctions_3_3_Core& gl = p->gl_;
gl.glUseProgram(p->shaderProgram_);
gl.glBindVertexArray(p->vao_);
gl.glDrawArrays(GL_TRIANGLES, 0, 3);
}
void TriangleLayer::deinitialize()
{
QOpenGLFunctions_3_3_Core& gl = p->gl_;
BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "deinitialize()";
gl.glDeleteProgram(p->shaderProgram_);
gl.glDeleteVertexArrays(1, &p->vao_);
gl.glDeleteBuffers(1, &p->vbo_);
p->shaderProgram_ = GL_INVALID_INDEX;
p->vao_ = GL_INVALID_INDEX;
p->vbo_ = GL_INVALID_INDEX;
}
} // namespace qt
} // namespace scwx

View file

@ -0,0 +1,31 @@
#include <qmapboxgl.hpp>
namespace scwx
{
namespace qt
{
class TriangleLayerImpl;
class TriangleLayer : public QMapbox::CustomLayerHostInterface
{
public:
explicit TriangleLayer();
~TriangleLayer();
TriangleLayer(const TriangleLayer&) = delete;
TriangleLayer& operator=(const TriangleLayer&) = delete;
TriangleLayer(TriangleLayer&&) noexcept;
TriangleLayer& operator=(TriangleLayer&&) noexcept;
void initialize() override final;
void render(const QMapbox::CustomLayerRenderParameters&) override final;
void deinitialize() override final;
private:
std::unique_ptr<TriangleLayerImpl> p;
};
} // namespace qt
} // namespace scwx