Skip to content

Commit 8e38568

Browse files
committed
Adding the functionality to copy the symbology raster and SVG to a
relative given path
1 parent 76564a8 commit 8e38568

File tree

2 files changed

+99
-1
lines changed

2 files changed

+99
-1
lines changed

libqfieldsync/offline_converter.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@
4949
from .layer import LayerSource, SyncAction
5050
from .offliners import BaseOffliner
5151
from .project import ProjectConfiguration, ProjectProperties
52-
from .utils.file_utils import copy_attachments, copy_multifile
52+
from .utils.file_utils import (
53+
copy_attachments,
54+
copy_multifile,
55+
update_symbol_to_project_assets,
56+
)
5357
from .utils.logger import logger
5458
from .utils.qgis import make_temp_qgis_file, open_project
5559
from .utils.xml import get_themapcanvas
@@ -280,6 +284,8 @@ def _convert(self, project: QgsProject) -> None:
280284
elif layer_action == SyncAction.REMOVE:
281285
project.removeMapLayer(layer)
282286

287+
update_symbol_to_project_assets(layer, self._export_filename.parent)
288+
283289
self.remove_empty_groups_from_layer_tree_group(project.layerTreeRoot())
284290

285291
export_project_filename = self._export_filename

libqfieldsync/utils/file_utils.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@
2929
from pathlib import Path
3030
from typing import List, Optional, Tuple, Union
3131

32+
from qgis.core import (
33+
QgsCategorizedSymbolRenderer,
34+
QgsProject,
35+
QgsRasterMarkerSymbolLayer,
36+
QgsRuleBasedRenderer,
37+
QgsSingleSymbolRenderer,
38+
QgsSvgMarkerSymbolLayer,
39+
QgsVectorLayer,
40+
QgsWkbTypes,
41+
)
3242
from qgis.PyQt.QtCore import QCoreApplication
3343

3444
from .exceptions import NoProjectFoundError, QFieldSyncError
@@ -216,3 +226,85 @@ def is_valid_filepath(path: str) -> bool:
216226
return False
217227

218228
return True
229+
230+
231+
def update_symbol_to_project_assets(
232+
layer: QgsVectorLayer, new_path: Optional[Path] = None
233+
) -> None:
234+
"""
235+
Updates the paths of symbols to a relative QGIS project
236+
"""
237+
238+
if (
239+
not isinstance(layer, QgsVectorLayer)
240+
or layer.geometryType() != QgsWkbTypes.PointGeometry
241+
):
242+
return
243+
244+
renderer = layer.renderer()
245+
246+
if not renderer:
247+
return
248+
249+
if new_path is None:
250+
project = QgsProject.instance()
251+
project_home = project.homePath()
252+
new_path = Path(project_home)
253+
destination_dir = new_path / "assets"
254+
destination_dir.mkdir(parents=True, exist_ok=True)
255+
256+
def update_symbol_path_assets(symbol):
257+
"""Updates the path of a symbol layer."""
258+
if symbol is None:
259+
return
260+
261+
for symbol_layer in symbol.symbolLayers():
262+
if isinstance(
263+
symbol_layer, (QgsSvgMarkerSymbolLayer, QgsRasterMarkerSymbolLayer)
264+
):
265+
source_path = Path(symbol_layer.path())
266+
267+
try:
268+
source_path.relative_to(new_path)
269+
continue
270+
except ValueError:
271+
# Proceed with copying
272+
pass
273+
274+
if source_path.exists():
275+
destination_path_file = destination_dir / source_path.name
276+
277+
if not destination_path_file.exists():
278+
shutil.copy2(source_path, destination_path_file)
279+
280+
symbol_layer.setPath(
281+
str(destination_path_file.relative_to(new_path))
282+
)
283+
284+
elif hasattr(symbol_layer, "subSymbol"):
285+
update_symbol_path_assets(
286+
symbol_layer.subSymbol()
287+
) # Recursively for nested symbology
288+
289+
if isinstance(renderer, QgsSingleSymbolRenderer):
290+
symbol = renderer.symbol()
291+
if symbol:
292+
update_symbol_path_assets(symbol)
293+
294+
elif isinstance(renderer, QgsRuleBasedRenderer):
295+
for rule in renderer.rootRule().children():
296+
if rule.symbol():
297+
update_symbol_path_assets(rule.symbol())
298+
299+
elif isinstance(renderer, QgsCategorizedSymbolRenderer):
300+
categories = renderer.categories()
301+
if categories:
302+
for index in range(len(categories)):
303+
# Get a fresh category object each time,
304+
# if not the new symbol path is not updated.
305+
category = renderer.categories()[index]
306+
symbol = category.symbol().clone()
307+
update_symbol_path_assets(symbol)
308+
renderer.updateCategorySymbol(index, symbol)
309+
310+
layer.setRenderer(renderer)

0 commit comments

Comments
 (0)