Files
MapLibreParticleLab/platform/qt/src/qquickmapboxgl.cpp
Thiago Marcos P. Santos e881795ed1 [Qt] Header cleanup (#6477)
Make some headers private, so we only commit with the QML API.
2016-09-27 16:18:04 +03:00

466 lines
12 KiB
C++

#include "qquickmapboxgl.hpp"
#include "qmapbox.hpp"
#include "qquickmapboxglmapparameter.hpp"
#include "qquickmapboxglrenderer.hpp"
#include <mbgl/util/constants.hpp>
#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 *)
{
m_error = QGeoServiceProvider::NotSupportedError;
m_errorString = "QQuickMapboxGL does not support plugins.";
emit errorChanged();
}
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 m_error;
}
QString QQuickMapboxGL::errorString() const
{
return m_errorString;
}
void QQuickMapboxGL::setVisibleRegion(const QGeoShape &shape)
{
m_visibleRegion = shape;
}
QGeoShape QQuickMapboxGL::visibleRegion() const
{
return m_visibleRegion;
}
void QQuickMapboxGL::setCopyrightsVisible(bool)
{
qWarning() << Q_FUNC_INFO << "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 {
m_error = QGeoServiceProvider::UnknownParameterError;
m_errorString = "Invalid source type: " + sourceType;
emit errorChanged();
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:
m_error = QGeoServiceProvider::UnknownParameterError;
m_errorString = "Invalid value for property 'type': " + param->property("type").toString();
emit errorChanged();
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) {
m_error = QGeoServiceProvider::NotSupportedError;
m_errorString = "QQuickMapboxGL::onParameterPropertyUpdated should be called from a QQuickMapboxGLMapParameter.";
emit errorChanged();
return;
}
if (propertyName == "type") {
m_error = QGeoServiceProvider::NotSupportedError;
m_errorString = "Property 'type' is a write-once.";
emit errorChanged();
return;
}
bool needsUpdate = false;
switch (s_parameterTypes.indexOf(param->property("type").toString())) {
case -1:
m_error = QGeoServiceProvider::UnknownParameterError;
m_errorString = "Invalid value for property 'type': " + param->property("type").toString();
emit errorChanged();
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);
}