457 lines
11 KiB
C++
457 lines
11 KiB
C++
#include "qquickmapboxglrenderer.hpp"
|
|
|
|
#include <mbgl/util/constants.hpp>
|
|
|
|
#include <QMapbox>
|
|
#include <QQuickMapboxGL>
|
|
#include <QQuickMapboxGLMapParameter>
|
|
|
|
#include <QDebug>
|
|
#include <QQuickItem>
|
|
#include <QRegularExpression>
|
|
#include <QString>
|
|
#include <QtGlobal>
|
|
#include <QQmlListProperty>
|
|
#include <QJSValue>
|
|
|
|
|
|
namespace {
|
|
|
|
static const QRegularExpression s_camelCase {"([a-z0-9])([A-Z])"};
|
|
static const QStringList s_parameterTypes = QStringList()
|
|
<< "style" << "paint" << "layout" << "layer" << "source" << "filter" << "image"
|
|
<< "bearing" << "pitch";
|
|
|
|
} // namespace
|
|
|
|
QQuickMapboxGL::QQuickMapboxGL(QQuickItem *parent_)
|
|
: QQuickFramebufferObject(parent_)
|
|
{
|
|
}
|
|
|
|
QQuickMapboxGL::~QQuickMapboxGL()
|
|
{
|
|
}
|
|
|
|
QQuickFramebufferObject::Renderer *QQuickMapboxGL::createRenderer() const
|
|
{
|
|
return new QQuickMapboxGLRenderer;
|
|
}
|
|
|
|
void QQuickMapboxGL::setPlugin(QDeclarativeGeoServiceProvider *)
|
|
{
|
|
qWarning() << __PRETTY_FUNCTION__
|
|
<< "Not implemented.";
|
|
}
|
|
|
|
QDeclarativeGeoServiceProvider *QQuickMapboxGL::plugin() const
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
void QQuickMapboxGL::componentComplete()
|
|
{
|
|
QQuickFramebufferObject::componentComplete();
|
|
|
|
for (const auto& param : m_parameters) {
|
|
processMapParameter(param);
|
|
}
|
|
}
|
|
|
|
void QQuickMapboxGL::setMinimumZoomLevel(qreal zoom)
|
|
{
|
|
zoom = qMax(mbgl::util::MIN_ZOOM, zoom);
|
|
zoom = qMin(m_maximumZoomLevel, zoom);
|
|
|
|
if (m_minimumZoomLevel == zoom) {
|
|
return;
|
|
}
|
|
|
|
m_minimumZoomLevel = zoom;
|
|
setZoomLevel(m_zoomLevel); // Constrain.
|
|
|
|
emit minimumZoomLevelChanged();
|
|
}
|
|
|
|
qreal QQuickMapboxGL::minimumZoomLevel() const
|
|
{
|
|
return m_minimumZoomLevel;
|
|
}
|
|
|
|
void QQuickMapboxGL::setMaximumZoomLevel(qreal zoom)
|
|
{
|
|
zoom = qMin(mbgl::util::MAX_ZOOM, zoom);
|
|
zoom = qMax(m_minimumZoomLevel, zoom);
|
|
|
|
if (m_maximumZoomLevel == zoom) {
|
|
return;
|
|
}
|
|
|
|
m_maximumZoomLevel = zoom;
|
|
setZoomLevel(m_zoomLevel); // Constrain.
|
|
|
|
emit maximumZoomLevelChanged();
|
|
}
|
|
|
|
qreal QQuickMapboxGL::maximumZoomLevel() const
|
|
{
|
|
return m_maximumZoomLevel;
|
|
}
|
|
|
|
void QQuickMapboxGL::setZoomLevel(qreal zoom)
|
|
{
|
|
zoom = qMin(m_maximumZoomLevel, zoom);
|
|
zoom = qMax(m_minimumZoomLevel, zoom);
|
|
|
|
if (m_zoomLevel == zoom) {
|
|
return;
|
|
}
|
|
|
|
m_zoomLevel = zoom;
|
|
|
|
m_syncState |= ZoomNeedsSync;
|
|
update();
|
|
|
|
emit zoomLevelChanged(m_zoomLevel);
|
|
}
|
|
|
|
qreal QQuickMapboxGL::zoomLevel() const
|
|
{
|
|
return m_zoomLevel;
|
|
}
|
|
|
|
void QQuickMapboxGL::setCenter(const QGeoCoordinate &coordinate)
|
|
{
|
|
if (m_center == coordinate) {
|
|
return;
|
|
}
|
|
|
|
m_center = coordinate;
|
|
|
|
m_syncState |= CenterNeedsSync;
|
|
update();
|
|
|
|
emit centerChanged(m_center);
|
|
}
|
|
|
|
QGeoCoordinate QQuickMapboxGL::center() const
|
|
{
|
|
return m_center;
|
|
}
|
|
|
|
QGeoServiceProvider::Error QQuickMapboxGL::error() const
|
|
{
|
|
return QGeoServiceProvider::NoError;
|
|
}
|
|
|
|
QString QQuickMapboxGL::errorString() const
|
|
{
|
|
return QString();
|
|
}
|
|
|
|
void QQuickMapboxGL::setVisibleRegion(const QGeoShape &shape)
|
|
{
|
|
m_visibleRegion = shape;
|
|
}
|
|
|
|
QGeoShape QQuickMapboxGL::visibleRegion() const
|
|
{
|
|
return m_visibleRegion;
|
|
}
|
|
|
|
void QQuickMapboxGL::setCopyrightsVisible(bool)
|
|
{
|
|
qWarning() << __PRETTY_FUNCTION__
|
|
<< "Not implemented.";
|
|
}
|
|
|
|
bool QQuickMapboxGL::copyrightsVisible() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void QQuickMapboxGL::setColor(const QColor &color)
|
|
{
|
|
if (m_color == color) {
|
|
return;
|
|
}
|
|
|
|
m_color = color;
|
|
|
|
StyleProperty change;
|
|
change.type = StyleProperty::Paint;
|
|
change.layer = "background";
|
|
change.property = "background-color";
|
|
change.value = color;
|
|
m_stylePropertyChanges << change;
|
|
|
|
update();
|
|
|
|
emit colorChanged(m_color);
|
|
}
|
|
|
|
QColor QQuickMapboxGL::color() const
|
|
{
|
|
return m_color;
|
|
}
|
|
|
|
void QQuickMapboxGL::pan(int dx, int dy)
|
|
{
|
|
m_pan += QPointF(dx, -dy);
|
|
|
|
m_syncState |= PanNeedsSync;
|
|
update();
|
|
}
|
|
|
|
void QQuickMapboxGL::onMapChanged(QMapbox::MapChange change)
|
|
{
|
|
if (change == QMapbox::MapChangeDidFinishLoadingStyle) {
|
|
m_styleLoaded = true;
|
|
update();
|
|
}
|
|
}
|
|
|
|
bool QQuickMapboxGL::parseImage(QQuickMapboxGLMapParameter *param)
|
|
{
|
|
m_imageChanges << Image {
|
|
param->property("name").toString(),
|
|
QImage(param->property("sprite").toString())
|
|
};
|
|
return true;
|
|
}
|
|
|
|
bool QQuickMapboxGL::parseStyle(QQuickMapboxGLMapParameter *param)
|
|
{
|
|
QString url = param->property("url").toString();
|
|
if (m_styleUrl == url) {
|
|
return false;
|
|
}
|
|
|
|
// Reload parameters if switching style URLs.
|
|
if (!m_styleUrl.isEmpty()) {
|
|
for (const auto& param_ : m_parameters) {
|
|
if (param_->property("type").toString() == "style") continue;
|
|
processMapParameter(param_);
|
|
}
|
|
}
|
|
|
|
m_styleUrl = url;
|
|
m_styleLoaded = false;
|
|
m_syncState |= StyleNeedsSync;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool QQuickMapboxGL::parseStyleProperty(QQuickMapboxGLMapParameter *param, const QString &name)
|
|
{
|
|
// Ignore meta-properties "type", "layer" and "class".
|
|
if (name == "type" || name == "layer" || name == "class") {
|
|
return false;
|
|
}
|
|
|
|
QString formattedName(name);
|
|
formattedName = formattedName.replace(s_camelCase, "\\1-\\2").toLower();
|
|
|
|
QVariant value = param->property(name.toLatin1());
|
|
if (value.canConvert<QJSValue>()) {
|
|
value = value.value<QJSValue>().toVariant();
|
|
}
|
|
|
|
m_stylePropertyChanges << QQuickMapboxGL::StyleProperty {
|
|
param->property("type").toString().at(0) == 'p' ? StyleProperty::Paint : StyleProperty::Layout,
|
|
param->property("layer").toString(),
|
|
formattedName,
|
|
value,
|
|
param->property("class").toString()
|
|
};
|
|
return true;
|
|
}
|
|
|
|
bool QQuickMapboxGL::parseStyleLayer(QQuickMapboxGLMapParameter *param)
|
|
{
|
|
QVariantMap layer;
|
|
layer["id"] = param->property("name");
|
|
layer["source"] = param->property("source");
|
|
layer["type"] = param->property("layerType");
|
|
if (param->property("sourceLayer").isValid()) {
|
|
layer["source-layer"] = param->property("sourceLayer");
|
|
}
|
|
m_layerChanges << layer;
|
|
return true;
|
|
}
|
|
|
|
bool QQuickMapboxGL::parseStyleSource(QQuickMapboxGLMapParameter *param)
|
|
{
|
|
QString sourceType = param->property("sourceType").toString();
|
|
|
|
QVariantMap source;
|
|
source["id"] = param->property("name");
|
|
source["type"] = sourceType;
|
|
if (sourceType == "vector" || sourceType == "raster") {
|
|
source["url"] = param->property("url");
|
|
m_sourceChanges << source;
|
|
} else if (sourceType == "geojson") {
|
|
QFile geojson(param->property("data").toString());
|
|
geojson.open(QIODevice::ReadOnly);
|
|
source["data"] = geojson.readAll();
|
|
m_sourceChanges << source;
|
|
} else {
|
|
qWarning() << "Error: Unsupported source type '" << sourceType << "'.";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool QQuickMapboxGL::parseStyleFilter(QQuickMapboxGLMapParameter *param)
|
|
{
|
|
QVariantMap filter;
|
|
filter["layer"] = param->property("layer");
|
|
filter["filter"] = param->property("filter");
|
|
m_filterChanges << filter;
|
|
return true;
|
|
}
|
|
|
|
bool QQuickMapboxGL::parseBearing(QQuickMapboxGLMapParameter *param)
|
|
{
|
|
qreal angle = param->property("angle").toReal();
|
|
if (m_bearing == angle) return false;
|
|
m_bearing = angle;
|
|
m_syncState |= BearingNeedsSync;
|
|
update();
|
|
return true;
|
|
}
|
|
|
|
bool QQuickMapboxGL::parsePitch(QQuickMapboxGLMapParameter *param)
|
|
{
|
|
qreal angle = param->property("angle").toReal();
|
|
angle = qMin(qMax(0., angle), mbgl::util::PITCH_MAX * mbgl::util::RAD2DEG);
|
|
if (m_pitch == angle) return false;
|
|
m_pitch = angle;
|
|
m_syncState |= PitchNeedsSync;
|
|
update();
|
|
return true;
|
|
}
|
|
|
|
void QQuickMapboxGL::processMapParameter(QQuickMapboxGLMapParameter *param)
|
|
{
|
|
bool needsUpdate = false;
|
|
switch (s_parameterTypes.indexOf(param->property("type").toString())) {
|
|
case -1:
|
|
qWarning() << "Error: Invalid value for property 'type'.";
|
|
break;
|
|
case 0: // style
|
|
needsUpdate |= parseStyle(param);
|
|
break;
|
|
case 1: // paint
|
|
case 2: // layout
|
|
for (int i = param->propertyOffset(); i < param->metaObject()->propertyCount(); ++i) {
|
|
needsUpdate |= parseStyleProperty(param, QString(param->metaObject()->property(i).name()));
|
|
}
|
|
break;
|
|
case 3: // layer
|
|
needsUpdate |= parseStyleLayer(param);
|
|
break;
|
|
case 4: // source
|
|
needsUpdate |= parseStyleSource(param);
|
|
break;
|
|
case 5: // filter
|
|
needsUpdate |= parseStyleFilter(param);
|
|
break;
|
|
case 6: // image
|
|
needsUpdate |= parseImage(param);
|
|
break;
|
|
case 7: // bearing
|
|
needsUpdate |= parseBearing(param);
|
|
break;
|
|
case 8: // pitch
|
|
needsUpdate |= parsePitch(param);
|
|
break;
|
|
}
|
|
if (needsUpdate) update();
|
|
}
|
|
|
|
void QQuickMapboxGL::onParameterPropertyUpdated(const QString &propertyName)
|
|
{
|
|
QQuickMapboxGLMapParameter *param = qobject_cast<QQuickMapboxGLMapParameter *>(sender());
|
|
if (!param) {
|
|
qWarning() << "Warning: Sender for QQuickMapboxGL::onParameterPropertyUpdated is not a QQuickMapboxGLMapParameter.";
|
|
return;
|
|
}
|
|
|
|
if (propertyName == "type") {
|
|
qWarning() << "Error: Cannot modify property 'type'.";
|
|
return;
|
|
}
|
|
|
|
bool needsUpdate = false;
|
|
switch (s_parameterTypes.indexOf(param->property("type").toString())) {
|
|
case -1:
|
|
qWarning() << "Error: Invalid value for property 'type'.";
|
|
break;
|
|
case 0: // style
|
|
needsUpdate |= parseStyle(param);
|
|
break;
|
|
case 1: // paint
|
|
case 2: // layout
|
|
needsUpdate |= parseStyleProperty(param, propertyName);
|
|
break;
|
|
case 3: // layer
|
|
needsUpdate |= parseStyleLayer(param);
|
|
break;
|
|
case 4: // source
|
|
needsUpdate |= parseStyleSource(param);
|
|
break;
|
|
case 5: // filter
|
|
needsUpdate |= parseStyleFilter(param);
|
|
break;
|
|
case 6: // image
|
|
needsUpdate |= parseImage(param);
|
|
break;
|
|
case 7: // bearing
|
|
needsUpdate |= parseBearing(param);
|
|
break;
|
|
case 8: // pitch
|
|
needsUpdate |= parsePitch(param);
|
|
break;
|
|
}
|
|
if (needsUpdate) update();
|
|
}
|
|
|
|
void QQuickMapboxGL::appendParameter(QQmlListProperty<QQuickMapboxGLMapParameter> *prop, QQuickMapboxGLMapParameter *param)
|
|
{
|
|
QQuickMapboxGL *map = static_cast<QQuickMapboxGL *>(prop->object);
|
|
map->m_parameters.append(param);
|
|
QObject::connect(param, SIGNAL(propertyUpdated(QString)), map, SLOT(onParameterPropertyUpdated(QString)));
|
|
}
|
|
|
|
int QQuickMapboxGL::countParameters(QQmlListProperty<QQuickMapboxGLMapParameter> *prop)
|
|
{
|
|
QQuickMapboxGL *map = static_cast<QQuickMapboxGL *>(prop->object);
|
|
return map->m_parameters.count();
|
|
}
|
|
|
|
QQuickMapboxGLMapParameter *QQuickMapboxGL::parameterAt(QQmlListProperty<QQuickMapboxGLMapParameter> *prop, int index)
|
|
{
|
|
QQuickMapboxGL *map = static_cast<QQuickMapboxGL *>(prop->object);
|
|
return map->m_parameters[index];
|
|
}
|
|
|
|
void QQuickMapboxGL::clearParameter(QQmlListProperty<QQuickMapboxGLMapParameter> *prop)
|
|
{
|
|
QQuickMapboxGL *map = static_cast<QQuickMapboxGL *>(prop->object);
|
|
for (auto param : map->m_parameters) {
|
|
QObject::disconnect(param, SIGNAL(propertyUpdated(QString)), map, SLOT(onParameterPropertyUpdated(QString)));
|
|
}
|
|
map->m_parameters.clear();
|
|
}
|
|
|
|
QQmlListProperty<QQuickMapboxGLMapParameter> QQuickMapboxGL::parameters()
|
|
{
|
|
return QQmlListProperty<QQuickMapboxGLMapParameter>(this,
|
|
nullptr,
|
|
appendParameter,
|
|
countParameters,
|
|
parameterAt,
|
|
clearParameter);
|
|
}
|