In 2015 we have already published a blog on this topic, which has met with great response. In the meantime a lot has changed in the QGIS world and with QGIS3 the 2015 manual can no longer be adopted 1:1. So I decided to write a new, revised blog article on this topic.

A lot of people are using QGIS Cloud as a service with ready to use QGIS webclient. It’s very easy to publish data and share maps in this way. Publishing of georeferenced images can be done with QGIS Cloud in a few steps as well. But the main problems are:

  • how to upload the binary images into the cloud database?
  • how to manage them?
  • how to display the results?

QGIS and QGIS Cloud are offering all tools for this task.

Create a new postgis layer with QGIS:

As the first step we need to create a point layer with some mandatory columns. The prerequisite for this step is the existence of a QGIS Cloud database. If you don’t have a QGIS Cloud database yet, create one in the QGIS Cloud plugin.

  • Open the QGIS DB Manager and select your QGIS Cloud database
  • Select Menue Table -> Create Table
  • name the new layer images
  • Create in minimum four columns:
    • id bigserial
    • geom (point)
    • name text
    • image text

Create_PostGIS_Layer

Create a new dialog with QtDesigner which looks like in the image below.

For adding and managing the images a customized input GUI is needed.

  • add two QLineEdit Widgets and name them with the corret objectName

  • add two QTextEdit Widgets and name them with the corret objectName

  • add a QToolButton Widget with the corret objectName

  • or download the file attribute.ui

QtDesigner

  • Open the layer properties of layer images and open the section fields
  • Open “Attributes Form”
  • Select: Provide ui-file
  • To get the custom UI select the path to attribute.ui in “Edit UI” and open the gui.
  • Click OK

layer_properties

Create a Python init function for handling the image data.

When you add a new feature to the layer images, the dialog opens but the tool button is not working. To get it working we need some Python code.

The idea behind that is to run some functionality to convert the binary image to text based base64 encoding. Let’s Python doing that job for us. QGIS offers the ability to define an init function when a custom dialog is opened. This offers a wide range of options.

  • create a new text file and call it encodeimage.py
  • add the following Python code (or download the Python script encodeimage.py):

Import all nesseccary Modules and Classes from PyQt5 and QGIS

from PyQt5.QtWidgets import QTextEdit,  QToolButton,  QFileDialog,  QLineEdit,  QLabel
import base64

This is the main function which will be connected with the QGIS layer dialog

def form_open( dialog, layer, featureid ):

    global name_field
    global image_field
    global image_preview
    global tool_button
    global file_name

Find all UI widgets and link them with the layer

    name_field = dialog.findChild( QLineEdit, "name" )
    file_name = dialog.findChild( QLineEdit, "filename" )
    image_field = dialog.findChild( QTextEdit, "image" )
    image_preview = dialog.findChild( QTextEdit, "imagePreview" )

Find the QToolButton Object to connect the conversion function.

    toolButton = dialog.findChild( QToolButton, "tool_button" )

When the ToolButton is clicked run the method get_file_name()

    toolButton.clicked.connect(get_file_name)

Set the imageField Widget invisible for a nicer GUI. It is not nesseccary to see the data

    image_field.setVisible(False)

Make the image preview visible with the HTML image tag

    image_preview.setText('<img width="300" src="data:image/jpg;base64,%s"/>' % (image_field.toPlainText()))

Open FileDialog and convert the binary image to base64

def get_file_name():
    file_path, filter = QFileDialog.getOpenFileName(None,  'Open Image File',  '',  '(Images *.jpg *.JPG)')

    if len(file_path) > 0:        
        file_name.setText(file_path)
        encoded_image = ''

Read the image file and proceed the encoding

        encoded_image = base64.b64encode(open(file_path, 'rb').read()).decode('utf-8')

Set the appropriate fields for storing in the QGIS layer

        image_field.setPlainText(encoded_image)

Set HTML img tag around the base64 image

        image_preview.setText('<img width="300" src="data:image/jpg;base64, %s"/>' % (str(encoded_image)))

If you have finished editing, save this file.

Open “Attributes Form” again and click on the small Python icon at the top of the form. Then the “Python init Code Configuration” dialog opens. As source of the Python init function select ‘Load from External File’. Then select the location of the file ‘encodeimage.py’. Finally, enter the name of the initialization function. In our case the function is called ‘form_open’. Finish the configuration with OK.

python_init_code_configuration

Alternatively to loading the initialization function from a file, you can also enter the program code directly in the dialog. Then the program code is only connected to this project. However, if you want to pass on the code or maintain the code, it is better to maintain the code in a separate file.

Now you are able to add images and photos to your spatialite database table. But be careful because the main disadvantage of this approach is the size of the image. The conversion of a 5Mb image produces a huge base64 string. As the result it will be very wise to reduce the size of the images you want to add to the layer. If you like, you can add a check of image size to the Python module or an automatically resize of images.

A nice tool of the QGIS map canvas is the option to add maptips to a layer. When maptips are activated the images should be visible when the mouse moves over a feature of the layer images in the map.

Create a maptip with image as content

  • open the layer properties of the layer images and jump to the Display section.
  • add the HTML code to the Text field as you can see in the figure below:
  • close the dialog with OK
  • activate the maptip tool in QGIS and move over a feature of the layer images. The name and the image are popped up.
	<table>
	<tr><td><b>[% "name" %]</b></td></tr>
	<tr><td><img width="400" src="data:image/jpg;base64,[% "image" %]"/></td></tr>
	</table>

layer_properties

Some additional settings

The image is saved in the images field. But this is only the base64 string. To prevent this from being displayed on an Identify in QGIS or in the QGIS Cloud Web Client we have to make some settings.

Prevent the images field from appearing in QGIS during an identify

To prevent a particular field from appearing in QGIS we need to reopen the layer settings of the images layer. There, select the Attributes Form section and check the image in the Fields section. The options appear on the right side of the dialog. Under Widget Type switch to Hidden. Now the image field no longer appears anywhere in QGIS.

Create a new virtual field with the HTML tag

Switch to the Fields section in the layer settings. Click on the field calculator icon and create a new virtual field of type Text (string).

As expression write:

'<img width="400" src="data:image/jpg;base64,' || "image" || ' "/>'

Share your images with QGIS Cloud

What’s about to share the pictures and the geolocation of the pictures with others over the web. For this task QGIS Cloud is the right choice. QGIS Cloud is your personal spatial data infrastructure and offers OGC Web Services and ready preconfigured Web GIS clients.

  • save the project.

  • open the QGIS Cloud plugin (If you don’t have the QGIS Cloud plugin installed, than install it from the official QGIS Plugin Repository)

  • log in your QGIS Cloud account. (If you don’t have a QGIS Cloud account, sign up a new account).

  • upload the local data to your QGIS Cloud database (if you don’t have a QGIS Cloud database, create one from the QGIS Cloud plugin).

  • publish the project via QGIS Cloud plugin.

  • open the map in QGIS Cloud Web Client

  • here you are, it works

Stay well

Dr. Horst Düster (@moazagotl)