-
-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
Observation
- I'm no expert in QGIS file formats / XML, so correct me if I'm wrong here.
- I swapped over from the XLSFormConverter plugin to using this, for generating projects to upload to QFieldCloud.
- The resulting projects load fine in QGIS and QField, but in the app.qfield.cloud UI I get an error:
- The resulting project is shown with a red dot, and cannot be viewed in QField when logged into the relevant account.
Analysis
- Digging into the generated
.qgsXML, I can see themapcanvaselement doesn't exist. - QFieldCloud's process_projectfile worker searches for this element by name to to extract the project extent and background colour.
- Projects generated with this plugin omit referencing the mapcanvas, as it doesn't involve a GUI (while the XLSFormConverter plugin did have an attached GUI context).
So either:
- This plugin should add a mapcontext element.
- QFieldCloud qgz processing should fail gracefully if no project extent or background colour is found.
I used Opus 4.6 to create a function that injects the mapcanvas element. The tool I am working on uses this & the projects are now green in QFC:
def _inject_map_canvas(
qgs_data: bytes, extent_bbox: list, log: logging.Logger
) -> tuple[bytes, bool]:
"""Inject <mapcanvas name="theMapCanvas"> if absent.
QFieldCloud's process_projectfile worker searches for this element by name
to extract the project extent and background colour. Projects generated by
xlsform2qgis omit it because QGIS only writes it when a QgsMapCanvas
object is attached (GUI context only).
"""
if b'name="theMapCanvas"' in qgs_data:
return qgs_data, False
# Grab the <spatialrefsys> block from <projectCrs> to reuse as <destinationsrs>
crs_m = re.search(
rb"<projectCrs>\s*(<spatialrefsys\b.*?</spatialrefsys>)\s*</projectCrs>",
qgs_data,
re.DOTALL,
)
if not crs_m:
log.warning(
"Cannot inject theMapCanvas: no <projectCrs> found in project XML"
)
return qgs_data, False
srs_block = crs_m.group(1)
# Derive map units from the CRS projection acronym
acronym_m = re.search(rb"<projectionacronym>(.*?)</projectionacronym>", srs_block)
acronym = acronym_m.group(1).decode() if acronym_m else ""
units = "degrees" if acronym == "longlat" else "meters"
xmin, ymin, xmax, ymax = extent_bbox
canvas_xml = (
f'\n <mapcanvas name="theMapCanvas" annotationsVisible="1">\n'
f" <units>{units}</units>\n"
f" <extent>\n"
f" <xmin>{xmin}</xmin>\n"
f" <ymin>{ymin}</ymin>\n"
f" <xmax>{xmax}</xmax>\n"
f" <ymax>{ymax}</ymax>\n"
f" </extent>\n"
f" <rotation>0</rotation>\n"
f" <destinationsrs>\n"
f" {srs_block.decode()}\n"
f" </destinationsrs>\n"
f" <rendermaptile>0</rendermaptile>\n"
f" </mapcanvas>"
).encode()
# Insert right after </verticalCrs> (preferred) or </projectCrs> (fallback)
for anchor in (b"</verticalCrs>", b"</projectCrs>"):
if anchor in qgs_data:
updated = qgs_data.replace(anchor, anchor + canvas_xml, 1)
log.info("Injected <mapcanvas name=\"theMapCanvas\"> into project XML")
return updated, True
log.warning("Could not find insertion point for theMapCanvas in project XML")
return qgs_data, FalseReactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels