Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tracking setup for mobile application #489

Merged
merged 9 commits into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 65 additions & 2 deletions Mergin/project_settings_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,27 @@
import os
from qgis.PyQt import uic
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QFileDialog
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtWidgets import QFileDialog, QMessageBox
from qgis.core import (
QgsProject,
QgsExpressionContext,
QgsExpressionContextUtils,
QgsFeature,
QgsFeatureRequest,
QgsExpression,
QgsVectorLayer,
)
from qgis.gui import QgsOptionsWidgetFactory, QgsOptionsPageWidget
from .attachment_fields_model import AttachmentFieldsModel
from .utils import icon_path, mergin_project_local_path, prefix_for_relative_path, resolve_target_dir
from .utils import (
icon_path,
mergin_project_local_path,
prefix_for_relative_path,
resolve_target_dir,
create_tracking_layer,
set_tracking_layer_flags,
)

ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "ui", "ui_project_config.ui")
ProjectConfigUiWidget, _ = uic.loadUiType(ui_file)
Expand Down Expand Up @@ -55,6 +64,21 @@ def __init__(self, parent=None):
idx = self.cmb_snapping_mode.findData(mode) if ok else 0
self.cmb_snapping_mode.setCurrentIndex(idx if idx > 0 else 0)

enabled, ok = QgsProject.instance().readBoolEntry("Mergin", "PositionTracking/Enabled")
if ok:
self.chk_tracking_enabled.setChecked(enabled)
else:
self.chk_tracking_enabled.setChecked(False)
self.chk_tracking_enabled.stateChanged.connect(self.check_project)

self.cmb_tracking_precision.addItem("Best", 0)
self.cmb_tracking_precision.addItem("Normal", 1)
self.cmb_tracking_precision.addItem("Low", 2)

mode, ok = QgsProject.instance().readNumEntry("Mergin", "PositionTracking/UpdateFrequency")
idx = self.cmb_tracking_precision.findData(mode) if ok else 1
self.cmb_tracking_precision.setCurrentIndex(idx)

self.local_project_dir = mergin_project_local_path()

if self.local_project_dir:
Expand Down Expand Up @@ -168,9 +192,47 @@ def update_preview(self, expression, layer, field_name):
else:
self.label_preview.setText(f"<i>{val}.jpg</i>")

def check_project(self, state):
"""
Check whether project is saved and we can create tracking
layer for it.
"""
if QgsProject.instance().absolutePath() == "":
QMessageBox.warning(
self,
"Project is not saved",
"It seems that your project is not saved yet, please save "
"project before enabling tracking as we need to know where "
"to place required files.",
)
self.chk_tracking_enabled.blockSignals(True)
self.chk_tracking_enabled.setCheckState(Qt.Unchecked)
self.chk_tracking_enabled.blockSignals(False)

def setup_tracking(self):
if self.chk_tracking_enabled.checkState() == Qt.Unchecked:
return

# check if tracking layer already exists
tracking_layer_id, ok = QgsProject.instance().readEntry("Mergin", "PositionTracking/TrackingLayer")
if tracking_layer_id != "" and tracking_layer_id in QgsProject.instance().mapLayers():
# tracking layer already exists in the project, make sure it has correct flags
layer = QgsProject.instance().mapLayers()[tracking_layer_id]
if layer is not None and layer.isValid():
set_tracking_layer_flags(layer)
return

# tracking layer does not exists or was removed from the project
# create a new layer and add it as a tracking layer
create_tracking_layer(QgsProject.instance().absolutePath())

def apply(self):
QgsProject.instance().writeEntry("Mergin", "PhotoQuality", self.cmb_photo_quality.currentData())
QgsProject.instance().writeEntry("Mergin", "Snapping", self.cmb_snapping_mode.currentData())
QgsProject.instance().writeEntry("Mergin", "PositionTracking/Enabled", self.chk_tracking_enabled.isChecked())
QgsProject.instance().writeEntry(
"Mergin", "PositionTracking/UpdateFrequency", self.cmb_tracking_precision.currentData()
)
for i in range(self.attachments_model.rowCount()):
index = self.attachments_model.index(i, 1)
if index.isValid():
Expand All @@ -181,3 +243,4 @@ def apply(self):
QgsProject.instance().writeEntry("Mergin", f"PhotoNaming/{layer_id}/{field_name}", expression)

self.save_config_file()
self.setup_tracking()
29 changes: 28 additions & 1 deletion Mergin/test/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@
import os
import json
import copy
import tempfile

from qgis.PyQt.QtCore import QVariant
from qgis.core import (
QgsProject,
QgsDatumTransform,
QgsCoordinateReferenceSystem,
QgsCoordinateTransformContext,
QgsVectorLayer,
QgsWkbTypes,
)

from qgis.testing import start_app, unittest
from Mergin.utils import same_schema, get_datum_shift_grids, is_valid_name
from Mergin.utils import same_schema, get_datum_shift_grids, is_valid_name, create_tracking_layer

test_data_path = os.path.join(os.path.dirname(__file__), "data")

Expand Down Expand Up @@ -170,6 +173,30 @@ def test_name_validation(self):
for t in test_cases:
self.assertEqual(is_valid_name(t[0]), t[1])

def test_create_tracking_layer(self):
with tempfile.TemporaryDirectory() as temp_dir:
file_path = create_tracking_layer(temp_dir)

self.assertTrue(os.path.exists(file_path))
dir_name, file_name = os.path.split(file_path)
self.assertEqual(file_name, "tracking_layer.gpkg")

layer = QgsVectorLayer(file_path, "", "ogr")
self.assertTrue(layer.isValid())
self.assertEqual(layer.wkbType(), QgsWkbTypes.LineStringZM)

fields = layer.fields()
self.assertEqual(len(fields), 5)
self.assertEqual(fields[0].name(), "fid")
self.assertEqual(fields[1].name(), "tracking_start_time")
self.assertEqual(fields[1].type(), QVariant.DateTime)
self.assertEqual(fields[2].name(), "tracking_end_time")
self.assertEqual(fields[2].type(), QVariant.DateTime)
self.assertEqual(fields[3].name(), "total_distance")
self.assertEqual(fields[3].type(), QVariant.Double)
self.assertEqual(fields[4].name(), "tracked_by")
self.assertEqual(fields[4].type(), QVariant.String)


if __name__ == "__main__":
nose2.main()
Loading
Loading