Просмотр исходного кода

Merge branch 'attribute-tracking' of kunickyd/vra-helper into master

Daniel Kunický 3 лет назад
Родитель
Сommit
2c39dcaa95

+ 2 - 0
src/core/CMakeLists.txt

@@ -21,6 +21,7 @@ set(QFIELD_CORE_SRCS
     appinterface.cpp
     attributeformmodel.cpp
     attributeformmodelbase.cpp
+    attributetrackingmodel.cpp
     badlayerhandler.cpp
     bluetoothdevicemodel.cpp
     bluetoothreceiver.cpp
@@ -108,6 +109,7 @@ set(QFIELD_CORE_HDRS
     appinterface.h
     attributeformmodel.h
     attributeformmodelbase.h
+    attributetrackingmodel.h
     badlayerhandler.h
     bluetoothdevicemodel.h
     bluetoothreceiver.h

+ 146 - 0
src/core/attributetrackingmodel.cpp

@@ -0,0 +1,146 @@
+#include "attributetrackingmodel.h"
+
+#include <QDebug>
+#include <QUrl>
+#include <QBuffer>
+#include "qgssymbol.h"
+#include "qgsrenderer.h"
+#include "qgscategorizedsymbolrenderer.h"
+
+
+AttributeTrackingModel::AttributeTrackingModel()
+    : QQuickImageProvider( Image ) { }
+
+QList<QObject *> AttributeTrackingModel::getLayerFields(QgsVectorLayer *layer)
+{
+    //qDebug() << "getLayerFields()";
+    QList<QObject *> fieldItems;
+
+    if(!layer){ //i should not allow calling this method with no layer context
+        //qDebug() << "no layer";
+        return fieldItems;
+    }
+
+    QgsFields fields = layer->fields();
+    
+    for(int i = 0 ; i < fields.count(); i++){
+        //qDebug() << "Name: " << fields[i].alias();
+        //qDebug() << "Value: " << fields[i].name();
+        fieldItems.append(new FieldItem(fields[i].name(), fields[i].alias()));
+    }
+    
+    //FieldItem test = *dynamic_cast<FieldItem *>(fieldItems[3]);
+
+    //qDebug() << test.alias();
+    //qDebug() << test.name();
+
+
+    return fieldItems;
+}
+
+void AttributeTrackingModel::startTracker(QgsVectorLayer *layer, QString inspectedAttribute, QModelIndex layerTreeIndex)
+{
+    if(!mTracker)
+        mTracker = new AttributeTracker();
+
+    mTracker->attribute = inspectedAttribute;
+    mTracker->layer = layer;
+    //mTracker->layerTreeIndex = layerTreeIndex;
+
+    emit trackerStarted(mTracker->layer, mTracker->attribute);
+}
+
+void AttributeTrackingModel::stopTracker()
+{
+    delete(mTracker);
+    mTracker = nullptr;
+
+    emit trackerStopped();
+}
+
+void AttributeTrackingModel::processIdentifyResults(QgsFeatureList results)
+{
+    if(results.count() < 1){
+        emit locationRecieved("NO FEATURE FOUND", "");
+        return;
+    }
+
+    QgsFeature result = results.first();
+
+    QString attributeValue = result.attribute(mTracker->attribute).toString();
+
+//    QgsRenderContext context = QgsRenderContext::fromMapSettings(mMapSettings->mapSettings());
+//    QgsFeatureRenderer * renderer =  mTracker->layer->renderer();
+//    QgsCategorizedSymbolRenderer* symbolRenderer = QgsCategorizedSymbolRenderer::convertFromRenderer(renderer);
+
+//    renderer->startRender(context, mTracker->layer->fields());
+//    QgsSymbol* symbol = renderer->originalSymbolForFeature(result, context);
+//    renderer->stopRender(context);
+
+
+
+    test(mTracker->layer->id());
+
+    //QImage image = symbol->asImage(QSize(40,40));
+
+    //QString image = mLayerTreeModel->data(mTracker->layerTreeIndex, FlatLayerTreeModel::LegendImage).toString();
+
+    emit locationRecieved(attributeValue, QString::number(result.id()));
+}
+
+void AttributeTrackingModel::recieveLocation(QgsPoint location)
+{
+    processIdentifyResults(identifyLayer(mTracker->layer, location));
+}
+
+bool AttributeTrackingModel::tracking()
+{
+    return mTracker != nullptr;
+}
+
+QgsQuickMapSettings *AttributeTrackingModel::mapSettings() const
+{
+    return mMapSettings;
+}
+
+void AttributeTrackingModel::setMapSettings(QgsQuickMapSettings *newMapSettings)
+{
+    test("setMapSettings");
+    mMapSettings = newMapSettings;
+}
+
+QImage AttributeTrackingModel::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
+{
+    if(id == "")
+        return QImage();
+
+    QgsRenderContext context = QgsRenderContext::fromMapSettings(mMapSettings->mapSettings());
+    QgsFeatureRenderer * renderer =  mTracker->layer->renderer();
+    QgsCategorizedSymbolRenderer* symbolRenderer = QgsCategorizedSymbolRenderer::convertFromRenderer(renderer);
+
+    renderer->startRender(context, mTracker->layer->fields());
+    QgsSymbol* symbol = renderer->originalSymbolForFeature(mTracker->layer->getFeature(id.toLongLong()), context);
+    renderer->stopRender(context);
+
+    QSize sizeValue = *size;
+
+    qDebug() << "size.width = " << size->width();
+    qDebug() << "size.height = " << size->height();
+
+    qDebug() << "requestedSize.width = " << requestedSize.width();
+    qDebug() << "requestedSize.height = " << requestedSize.height();
+
+    return symbol->asImage(QSize(requestedSize.width() / 5, requestedSize.height() / 5));
+}
+
+void AttributeTrackingModel::test(QString msg)
+{    
+    qDebug() << msg;
+}
+
+
+
+
+
+
+

+ 127 - 0
src/core/attributetrackingmodel.h

@@ -0,0 +1,127 @@
+#ifndef ATTRIBUTETRACKINGMODEL_H
+#define ATTRIBUTETRACKINGMODEL_H
+
+#include <QAbstractItemModel>
+#include <QUrl>
+#include <QBuffer>
+#include <QQuickImageProvider>
+#include "qgsvectorlayer.h"
+#include "qgsquickmapsettings.h"
+#include "layertreemodel.h"
+
+class FieldItem : public QObject{
+
+    Q_OBJECT
+    Q_PROPERTY(QString name READ name )
+    Q_PROPERTY(QString alias READ alias )
+
+public:
+    FieldItem(QString name, QString alias)
+
+    {
+        this->mName = name;
+        this->mAlias = alias;
+    }
+
+    FieldItem(const FieldItem &field)
+    {
+        this->mName = field.name();
+        this->mAlias = field.alias();
+    }
+
+    FieldItem& operator=(FieldItem other)
+    {
+        this->mName = other.name();
+        this->mAlias = other.alias();
+
+        return *this;
+    }
+
+    QString name() const { return mName; }
+    QString alias() const { return mAlias; }
+
+private:
+    QString mName;
+    QString mAlias;
+
+};
+
+class AttributeTracker{
+
+public:
+    QgsVectorLayer* layer;
+    QString attribute;
+//    QModelIndex layerTreeIndex;
+};
+
+class AttributeTrackingModel : public QObject, public QQuickImageProvider
+{
+    Q_OBJECT
+
+public:
+    AttributeTrackingModel(/*FlatLayerTreeModel* layerTreeModel*/);
+
+    Q_INVOKABLE QList<QObject *> getLayerFields(QgsVectorLayer *layer);
+    Q_INVOKABLE bool tracking();
+
+    Q_INVOKABLE QgsQuickMapSettings *mapSettings() const;
+    Q_INVOKABLE void setMapSettings(QgsQuickMapSettings *newMapSettings);
+
+    QUrl imageToUrl(const QImage& image);
+
+    QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize);
+
+public slots:
+    void startTracker(QgsVectorLayer *layer, QString inspectedAttribute, QModelIndex layerTreeIndex);
+    void stopTracker();
+    void processIdentifyResults(QgsFeatureList results);
+    void recieveLocation(QgsPoint location);
+    void test(QString msg);
+
+signals:
+    void trackerStarted(QgsVectorLayer *layer, QString attribute);
+    void trackerStopped();
+    void locationRecieved(QString attributeValue, QString featureId);
+
+private:    
+    QgsFeatureList identifyLayer(QgsVectorLayer *layer, QgsPoint point)
+    {
+        int searchRadius = 10;
+
+        QgsFeatureList featureList;
+
+        QgsRectangle r;
+        r.setXMinimum( point.x() - searchRadius );
+        r.setXMaximum( point.x() + searchRadius );
+        r.setYMinimum( point.y() - searchRadius );
+        r.setYMaximum( point.y() + searchRadius );
+
+        QgsFeatureRequest req;
+        req.setFilterRect( r );
+        req.setLimit( 100 );
+        req.setFlags( QgsFeatureRequest::ExactIntersect );
+
+        QgsFeatureIterator fit = layer->getFeatures( req );
+        QgsFeature f;
+        while ( fit.nextFeature( f ) )
+          featureList << QgsFeature( f );
+
+        return featureList;
+    }
+
+    QUrl imageToUrl(QImage image) const
+    {
+        QByteArray byteArray;
+        QBuffer buffer(&byteArray);
+        buffer.open(QIODevice::WriteOnly);
+        image.save(&buffer, "png");
+        QString base64 = QString::fromUtf8(byteArray.toBase64());
+        return QString("data:image/png;base64,") + base64;
+    }
+
+    AttributeTracker* mTracker = nullptr;
+    QgsQuickMapSettings* mMapSettings = nullptr;
+//    FlatLayerTreeModel* mLayerTreeModel = nullptr;
+};
+
+#endif // ATTRIBUTETRACKINGMODEL_H

+ 6 - 0
src/core/qgismobileapp.cpp

@@ -46,6 +46,7 @@
 
 #include "appinterface.h"
 #include "attributeformmodel.h"
+#include "attributetrackingmodel.h"
 #include "badlayerhandler.h"
 #include "bluetoothdevicemodel.h"
 #include "bluetoothreceiver.h"
@@ -219,6 +220,7 @@ QgisMobileapp::QgisMobileapp( QgsApplication *app, QObject *parent )
   mFlatLayerTree = new FlatLayerTreeModel( mProject->layerTreeRoot(), mProject, this );
   mLegendImageProvider = new LegendImageProvider( mFlatLayerTree->layerTreeModel() );
   mTrackingModel = new TrackingModel;
+  mAttributeTrackingModel = new AttributeTrackingModel;
 
   // Transition from 1.8 to 1.8.1+
   const QString deviceAddress = settings.value( QStringLiteral( "positioningDevice" ), QString() ).toString();
@@ -464,6 +466,8 @@ void QgisMobileapp::initDeclarative()
   qmlRegisterUncreatableType<QgsGpkgFlusher>( "org.qfield", 1, 0, "QgsGpkgFlusher", "The gpkgFlusher is available as context property `gpkgFlusher`" );
   qmlRegisterUncreatableType<LayerObserver>( "org.qfield", 1, 0, "LayerObserver", "" );
   qmlRegisterUncreatableType<DeltaFileWrapper>( "org.qfield", 1, 0, "DeltaFileWrapper", "" );
+  qmlRegisterUncreatableType<FieldItem>("org.qfield", 1, 0, "FieldItem", "");
+
 
   qRegisterMetaType<SnappingResult>( "SnappingResult" );
 
@@ -492,8 +496,10 @@ void QgisMobileapp::initDeclarative()
   rootContext()->setContextProperty( "qfieldAuthRequestHandler", mAuthRequestHandler );
 
   rootContext()->setContextProperty( "trackingModel", mTrackingModel );
+  rootContext()->setContextProperty( "attributeTrackingModel", mAttributeTrackingModel );
 
   addImageProvider( QLatin1String( "legend" ), mLegendImageProvider );
+  addImageProvider( QLatin1String( "attributeTracking" ), mAttributeTrackingModel );
 }
 
 void QgisMobileapp::loadProjectQuirks()

+ 2 - 0
src/core/qgismobileapp.h

@@ -39,6 +39,7 @@
 #include "screendimmer.h"
 #include "settings.h"
 
+class AttributeTrackingModel;
 class AppInterface;
 class AppMissingGridHandler;
 class QgsOfflineEditing;
@@ -189,6 +190,7 @@ class QFIELD_CORE_EXPORT QgisMobileapp : public QQmlApplicationEngine
     QgsExifTools mExifTools;
 
     TrackingModel *mTrackingModel = nullptr;
+    AttributeTrackingModel *mAttributeTrackingModel = nullptr;
 
     AppMissingGridHandler *mAppMissingGridHandler = nullptr;
 

+ 101 - 0
src/qml/AttributeTracker.qml

@@ -0,0 +1,101 @@
+import QtQuick 2.0
+
+import org.qgis 1.0
+import org.qfield 1.0
+
+Item {
+    id: root
+    anchors.fill: parent
+
+    property variant location
+    property VectorLayer currentLayer
+    property var currentAttribute
+    property var symbolImage
+    property var currentAttributeValue
+    property MapSettings settings
+    
+
+
+    Connections{
+        target: attributeTrackingModel
+
+        function onTrackerStarted(layer, attribute) {
+            attributeTrackingModel.test("onTrackerStarted");
+
+            attributeTrackingModel.setMapSettings(settings);
+
+            currentLayer = layer;
+            currentAttribute = attribute;
+
+            trackingBanner.active = true;
+        }
+
+        function onTrackerStopped() {
+            attributeTrackingModel.test("onTrackerStopped");
+            trackingBanner.active = false;
+        }
+
+        function onLocationRecieved(attributeValue, featureId){
+             attributeTrackingModel.test("onLocationRecieved");
+            symbolImage = featureId;
+            currentAttributeValue = attributeValue;
+        }
+    }
+    
+    Loader{
+        id: trackingBanner
+        sourceComponent: banner
+        active: false
+        onLoaded: {
+            item.visible = true;            
+        }
+    }
+
+    Component{
+        id: banner
+
+        Item{            
+            property variant location: root.location
+
+            parent: root
+            anchors.fill: parent
+
+            anchors.margins: 10
+            anchors.bottomMargin: 55
+
+            Image{
+
+                anchors.bottom: parent.bottom
+
+                //color: symbolColor
+                height: mainWindow.height / 3
+                width: mainWindow.width - 20
+
+                sourceSize.height: mainWindow.height / 3
+                sourceSize.width: mainWindow.width - 20
+
+
+                source: "image://attributeTracking/" + symbolImage
+
+
+                Rectangle{
+                    color: "white"
+                    anchors.centerIn: parent
+                    width: attributeTrackerFieldLabel.width + 5
+                    height: attributeTrackerFieldLabel.height + 5
+
+                    Text {
+                        id: attributeTrackerFieldLabel
+                        text: currentAttribute +": "+ currentAttributeValue
+                        anchors.centerIn: parent
+                    }
+                }
+            }
+
+            onLocationChanged: {
+                attributeTrackingModel.test("onLocationChanged");               
+                attributeTrackingModel.recieveLocation(location);
+            }
+        }
+    }
+}

+ 94 - 0
src/qml/AttributeTrackingSettings.qml

@@ -0,0 +1,94 @@
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+import QtQuick.Layouts 1.12
+import org.qgis 1.0
+import org.qfield 1.0
+
+import Theme 1.0
+
+
+Popup{
+    id: popup
+    parent: ApplicationWindow.overlay
+
+    property VectorLayer currentLayer
+    property var index
+
+    x: 24
+    y: 24
+    padding: 0
+    width: parent.width - 48
+    height: parent.height - 48
+    modal: true
+    closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
+
+    Page{
+        focus: true
+        anchors.fill: parent
+
+        header: PageHeader {
+            title: qsTr("Attribute Tracker Settings")
+
+            showApplyButton: false
+            showCancelButton: false
+            showBackButton: true
+
+            onBack: {
+                popup.close()
+            }
+        }
+
+        ScrollView {
+            padding: 20
+            ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
+            ScrollBar.vertical.policy: ScrollBar.AsNeeded
+            contentWidth: trackerSettingsGrid.width
+            contentHeight: trackerSettingsGrid.height
+            anchors.fill: parent
+            clip: true
+
+            GridLayout {
+                id: trackerSettingsGrid
+                width: parent.parent.width
+                Layout.fillWidth: true
+
+                columns: 2
+                columnSpacing: 0
+                rowSpacing: 5
+
+                Label{
+                    text: qsTr("Choose attribute:")
+                }
+
+                ComboBox{
+                    id: attributeSelector
+
+                    textRole: "name"
+                    valueRole: "name"
+                    model: attributeTrackingModel.getLayerFields(currentLayer)
+                }
+
+                QfButton {
+                  id: saveButton
+                  Layout.topMargin: 8
+                  Layout.fillWidth: true
+                  Layout.columnSpan: 2
+                  font: Theme.defaultFont
+                  text: qsTr( "Start tracking")
+                  //icon.source: Theme.getThemeVectorIcon( 'directions_walk_24dp' )
+
+                  onClicked: {                      
+                    attributeTrackingModel.startTracker(currentLayer, attributeSelector.currentValue, index);
+                    dashBoard.visible = false;
+                    close();
+                  }
+                }
+            }
+        }
+    }
+}
+
+
+
+
+

+ 36 - 0
src/qml/LayerTreeItemProperties.qml

@@ -21,6 +21,10 @@ Popup {
   property bool trackingButtonVisible: false
   property var trackingButtonText
 
+  property var attributeTrackingButtonText
+  property bool attributeTrackingButtonVisible: false
+
+
   width: Math.min( childrenRect.width, mainWindow.width - 20 )
   x: (parent.width - width) / 2
   y: (parent.height - height) / 2
@@ -43,6 +47,11 @@ Popup {
     trackingButtonText = trackingModel.layerInTracking( layerTree.data(index, FlatLayerTreeModel.VectorLayerPointer) )
         ? qsTr('Stop tracking')
         : qsTr('Setup tracking')
+
+    attributeTrackingButtonVisible = isAttributeTrackingButtonVisible();
+    attributeTrackingButtonText = attributeTrackingModel.tracking()
+        ? qsTr('Stop attribute tracking')
+        : qsTr('Setup attribute tracking')
   }
 
   Page {
@@ -214,6 +223,26 @@ Popup {
         }
       }
 
+      QfButton {
+          id: setupAttributeTracking
+          Layout.fillWidth: true
+          Layout.topMargin: 5
+          text: attributeTrackingButtonText
+          visible: attributeTrackingButtonVisible
+          onClicked: {
+              if(attributeTrackingModel.tracking()){
+                  attributeTrackingModel.stopTracker();
+                  dashBoard.visible = false;
+              }else{
+                  attributeSettingsDialog.currentLayer = layerTree.data(index, FlatLayerTreeModel.VectorLayerPointer);
+                  attributeSettingsDialog.index = index;
+                  attributeSettingsDialog.open();
+              }
+              close();
+
+          }
+      }
+
       Text {
         id: lockText
         property var padlockIcon: Theme.getThemeIcon('ic_lock_black_24dp')
@@ -306,4 +335,11 @@ Popup {
     return layerTree.data( index, FlatLayerTreeModel.IsValid )
         && layerTree.data( index, FlatLayerTreeModel.LayerType ) === 'vectorlayer'
   }
+
+  function isAttributeTrackingButtonVisible(){
+      //TODO: find out which layer types are supported
+      return layerTree.data( index, FlatLayerTreeModel.Type ) === 'layer'
+              && layerTree.data(index, FlatLayerTreeModel.VectorLayerPointer).geometryType() === QgsWkbTypes.PolygonGeometry
+              && positionSource.active;
+  }
 }

+ 10 - 0
src/qml/qgismobileapp.qml

@@ -823,6 +823,16 @@ ApplicationWindow {
     }
   }
 
+  AttributeTrackingSettings{
+      id: attributeSettingsDialog
+  }
+
+  AttributeTracker{
+      id: attributeTracker
+      location: positionSource.projectedPosition
+      settings: mapCanvas.mapSettings
+  }
+
   /* The main menu */
   Row {
     id: mainMenuBar

+ 2 - 0
src/qml/qml.qrc

@@ -72,5 +72,7 @@
         <file>QFieldCloudPopup.qml</file>
         <file>QFieldCloudDeltaHistory.qml</file>
         <file>QFieldCloudExportLayersFeedback.qml</file>
+        <file>AttributeTrackingSettings.qml</file>
+        <file>AttributeTracker.qml</file>
     </qresource>
 </RCC>