3

We have a large amount of background mapping data saved in a OneDrive folder that is then synced to each user's laptop on their C drive. If one user saves a QGIS workspace on the company's shared "G:" drive containing some of these layers (example layer source C:\Users\Bob\...) and then another user later opens the same workspace, QGIS looks for Bob's files, when I'd like it to look for the files under C:\Users\Alice\...

Is it possible to save a workspace with a dynamic source for these layers such as "C:\Users\%USERPROFILE%\" or similar?

2
  • QGIS uses relative file paths by default so as long as (a) the project is in the %USERPROFILE% folder or below, and (b) in the same location on each user's computer relative to the shared files (e.g. C:\Users\Bob\GIS\project.qgz and C:\Users\Alice\GIS\project.qgz), then it shouldn't need to access dynamic user names. Any files under %USERPROFILE%\OneDrive - OrgName` (where %USERPROFILE%` = C:\Users\Alice etc.) would be accessed using e.g. `..\..\OneDrive - OrgName` regardless of whether it's Bob or Alice
    – she_weeds
    Feb 21 at 12:49
  • Thanks, but the workspace will be saved in the relevant project folder on the network drive - G:\ - so the paths for the background mapping layers are saved with the full user profile each time resulting in them being broken if a different user opens the workspace. Feb 21 at 13:03

1 Answer 1

2

This is an interesting question. Unfortunately, QGIS cannot resolve environment variables, but it can repair invalid paths during project load. The problem can be solved with the help of a small Python plugin.

Use the QGIS Python plugin builder (https://plugins.qgis.org/plugins/pluginbuilder/) and create a plugin named "changeFilePath". Then add the following code lines to changeFilePath.py:

from qgis.core import QgsPathResolver,QgsProject,QgsMessageLog,Qgis
import re,os

class changeFilePath:
    """QGIS Plugin Implementation."""

    def __init__(self, iface):
        pass

    def initGui(self):
        self.processor = QgsPathResolver.setPathPreprocessor(self.my_processor)

    def unload(self):
        QgsPathResolver.removePathPreprocessor(self.processor)

    def my_processor(self,path):
        try:
          # if file not found
          if not os.path.isfile(path):
              # if path is local users path
              if re.search(r'^[cd][:][\\/]users[\\/][^\\/]+',path,flags=re.IGNORECASE):
                  QgsMessageLog.logMessage('Replace Layer Source: %s' % path,'Custom',Qgis.Info)
                  userprofile = os.getenv("USERPROFILE")
                  userprofile = userprofile.encode('unicode_escape').decode('ascii')
                  userprofile = re.sub(r'\\u(.){4}', '', userprofile)
                  path = re.sub(r'^[cd][:][\\/]users[\\/][^\\/]+',userprofile,path,flags=re.IGNORECASE)
                  QgsMessageLog.logMessage('with: %s' % path,'Custom',Qgis.Info)
        except:
          pass
        return path

The local user directory can be located on C: or D: drive.

3
  • Thanks! I've already developed a company plugin for QGIS so I'll look to include this code in that. Feb 22 at 9:08
  • Yes, that's the way to go. I have a company plugin that replaces PostGIS database names and repairs QGIS projects that were stored in GPKG files together with the data. When you move a GPKG project to a different place, QGIS won't find the data inside.
    – christoph
    Feb 22 at 10:11
  • I learned my lesson today, never share code untested! re.sub produced a fatal error because of a unicode userprofile string. I added 2 lines of code to prepare the userprofile string. Be aware that every error in my_processor will be fatal.
    – christoph
    Feb 22 at 12:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.