I'm trying to import spatial bookmarks from the QGIS Python console, adapted from this thread and this blog, for the text file I created of exported bookmarks from Arc

import sqlite3
dbPath = QgsApplication.qgisUserDatabaseFilePath() 
db = sqlite3.connect(dbPath)
cursor = db.cursor()

srcFile = "D:\\Folder\\filename.txt"

with open(srcFile,'r') as infile:
    bkm = infile.readlines()

for b in bkm:
    name,project_name,xmin,ymin,xmax,ymax,EPSG = b.split(',')
    cursor.execute("""INSERT INTO tbl_bookmarks(bookmark_id, name, group, xmin, ymin, xmax, ymax,projection_srid)VALUES(NULL, str(name), str(project_name),float(xmin),float(ymin),float(xmax),float(ymax),int(EPSG))""")

But I get the following error:

Traceback (most recent call last): File "C:\PROGRA~1\QGIS 3.16\apps\Python37\lib\code.py", line 90, in runcode exec(code, self.locals) File "", line 1, in File "", line 13, in sqlite3.OperationalError: near "group": syntax error

Which I'm not sure what to make of.

I've tried various iterations of name capitalization (e.g. "Group" instead of "group") consistent with the column names screen cap of Spatial Bookmarks Manager column names But with no joy.

Any thoughts?

PS. My other thought is to write to XML out of Arc instead of text and import via the QGIS Spatial Bookmarks Manager GUI:

enter image description here

But prefer the console-based approach.

  • Is your previous question still 'on' ?
    – Snaileater
    Commented May 6, 2021 at 9:51
  • I'm keen for a solution that takes my current text file. Prefer not to rewrite my other code to XML. Happy for your thoughts.
    – CreekGeek
    Commented May 6, 2021 at 9:57
  • @Snaileater if interested, the post that originally asked how to get bookmarks from Arc to QGIS has been re-titled to focus only on the export from side of the process: gis.stackexchange.com/q/395795/17482
    – CreekGeek
    Commented May 6, 2021 at 19:21

3 Answers 3


A solution using CSV

First recipe not useful in your case. It exports project bookmarks to CSV within QGIS (change path to file if you want to use it). In your case, you should better look at 2nd recipe that read your CSV and add it. I got a remaining issue about refreshing corresponding bookmark panel in the GUI.

You also need to be aware there are 2 entry points:

  • QgsProject.instance().bookmarkManager() for project bookmarks
  • QgsApplication.bookmarkManager() for user bookmarks

I've use QgsProject.instance().bookmarkManager() in my sample code so you may change it to replace it with the other entry or add a conditional statement to choose destination.

# Export project bookmarks to CSV
import os

bookmarks = QgsProject.instance().bookmarkManager().bookmarks()

referencedRectangleKeys = ['xMinimum', 'yMinimum', 'xMaximum', 'yMaximum']
csv_header = ['bookmark_id', 'name', 'group', 'xmin', 'ymin', 'xmax', 'ymax', 'projection_srid']

content = []
for bookmark in bookmarks:
    referencedRectangle = bookmark.extent()
    xmin, ymin, xmax, ymax = [getattr(referencedRectangle, val)() for val in referencedRectangleKeys]
    csv_line = [bookmark.id(), bookmark.name(), bookmark.group(), xmin, ymin, xmax, ymax, referencedRectangle.crs().authid()]
    content.append([str(el) for el in csv_line])

content = [csv_header] + content
with open('/tmp/export_project_bookmarks.csv', 'w') as outfile:
    outfile.write(f"{os.linesep.join([','.join(subcontent) for subcontent in content])}")

This recipe imports project bookmarks from CSV (change path to file). Caution: SRID looks like EPSG:3857 not just 3857. Change code if necessary

# Import project bookmarks from CSV
import csv

bookmarkManager = QgsProject.instance().bookmarkManager()
with open('/tmp/export_project_bookmarks.csv') as csvfile:
    bookmarkReader = csv.DictReader(csvfile, delimiter=',', quotechar='"')
    for row in bookmarkReader:
        b = QgsBookmark()
        extent = QgsReferencedRectangle(
            QgsRectangle(float(row['xmin']), float(row['ymin']), float(row['xmax']), float(row['ymax'])),

You can take the following CSV as an input

{1315633b-a20f-49a1-8a02-d4a397cc668c},New bookmark,,-5451731.1615,-1367345.8844,19199574.9599,7949826.7695,EPSG:3857
  • Cheers for post...this actually comes the closest to addressing the title of the thread. I added a header row to the csv with the same names as your recipe as well as an id column. However, I get: Traceback (most recent call last): File "C:\PROGRA~1\QGIS 3.16\apps\Python37\lib\code.py", line 90, in runcode exec(code, self.locals) File "<input>", line 1, in <module> File "<string>", line 11, in <module> KeyError: 'bookmark_id' I've tried several options for formatting the 'bookmark_id' column, e.g. "1", 1, NULL, and "NULL", but all keep throwing the same error.
    – CreekGeek
    Commented May 9, 2021 at 23:14
  • Did you firstly try with the sample CSV on your own machine? Then, it will be about the difference between your input content and mine?
    – ThomasG77
    Commented May 9, 2021 at 23:19
  • Yes. The error (pasted into comment) was associated with all of my attempts to use my own csv (where I added an id column and a header row with your values). Given I achieved my overall goal (bookmarks from Arc to QGIS) by rewriting code from the Arc-export end to output XML, I'm inclined to table further troubleshooting of the txt-based approach as I'm on a deadline. Cheers.
    – CreekGeek
    Commented May 10, 2021 at 2:46

If you can import manually via the QGIS Spatial Bookmarks Manager, then you can import the same bookmark file using this line:

  • cheers for reply. My intent was to use the files I already had and avoid having to write an xml file.
    – CreekGeek
    Commented May 9, 2021 at 20:04
  • @CreekGeek The code reads from xml file, not write. Commented May 9, 2021 at 20:09
  • I totally understand. But since I didn't have XML files, that wouldn't work. I hit diminishing returns on resolving txt import, so I've now amended my output code from Arc to write directly to XML (posted separately in this thread). Cheers
    – CreekGeek
    Commented May 9, 2021 at 20:39

I was unable to figure the proper syntax for looping through the text file via the QGIS console.

STEP 1: I revisited my code to export from Arc to write directly to xml formatted as the QGIS Spatial Bookmarks Manager expects:

import arcpy
import xml.etree.ElementTree as ET

srcPath = "D:\\SomeFolder\\Data\\"
srcFile = "MyMap.mxd"

arcpy.env.overwriteOutput = True

# set working mxd
mxd = arcpy.mapping.MapDocument(srcPath+srcFile)
print("Working mxd path is: " + srcPath + srcFile)

# create top-level tag for xml file, QGIS expects it to be called 'qgis_bookmarks'
top = ET.Element('qgis_bookmarks')

sr_id=177 # id number for desired CRS ....NOTE: does NOT equal CRS number
project = "Some Project" # optional

# iterate through all dataframes in an mxd
for df in arcpy.mapping.ListDataFrames(mxd,"*"):
    # iterate through all bookmarks in a dataframe
    for bkmk in arcpy.mapping.ListBookmarks(mxd, "", df):

        # get extents of bookmark
        e = bkmk.extent

        # create a temporary list of tuples with location attributes
        # the first item of each tuple is the field name QGIS expects for its sqlite db
        tmp_lst = [("id",""),

        # create second level tag, QGIS expects it to be called 'bookmark' 
        child = ET.SubElement(top, 'bookmark')

        # iterate through list to create location tags for each bookmark
        for i in tmp_lst:
            subchild = ET.SubElement(child, i[0]) # create third level tag of requisite QGIS name
            subchild.text = (str(i[1])) # populate value of third level tag

ET.ElementTree(top).write(srcPath + "bookmarks\\sample.xml")

STEP 2: With xml file in-hand, I found it fastest to import via the Spatial Bookmarks Manager GUI (see highlighted graphic in my original post).

If wanting to script, could also do step 2 from the console as indicated in the post by @Kadir Sabhaz,

  • @PolyGeo I kinda came full-circle on this from my original post in that other thread last week (get bookmarks from Arc to QGIS). Given how I've sorted the problem (above), maybe best to amend the title of this thread?
    – CreekGeek
    Commented May 9, 2021 at 20:44

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.