Skip to content
Permalink
Browse files

[FEATURE][processing] Import geotagged photos algorithm

This implements a new "import geotagged photos" algorithm
for processing. It allows selection of a folder which it
will scan for jpg files which have been geotagged and
creates a PointZ layer with the result, with attributes
for photo path, altitude, direction and timestamp.

Optionally the scan can be recursive and you can create
an optional table of photos which could not be read
or which were missing geotags.

The algorithm automatically sets the output table to
use an external resource widget to display the linked
photos in the attribute form.

[ALGCHANGE]
  • Loading branch information
nyalldawson committed Mar 14, 2018
1 parent 87e8432 commit fa2c5aebb70b7ab1c865fbcb7713f73457510847
@@ -440,7 +440,6 @@ Copy member variables from other raster data provider. Useful for implementation




};

QFlags<QgsRasterDataProvider::ProviderCapability> operator|(QgsRasterDataProvider::ProviderCapability f1, QFlags<QgsRasterDataProvider::ProviderCapability> f2);
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
a
Binary file not shown.
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ import_photos.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>149.1318777777778</gml:X><gml:Y>-37.2305</gml:Y><gml:Z>422.191</gml:Z></gml:coord>
<gml:coord><gml:X>149.2751666666667</gml:X><gml:Y>-36.22089166666667</gml:Y><gml:Z>867</gml:Z></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:import_photos fid="import_photos.0">
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>149.275166666667,-37.2305,422.191</gml:coordinates></gml:Point></ogr:geometryProperty>
<ogr:photo>/home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/custom/photos/0997.JPG</ogr:photo>
<ogr:filename>0997</ogr:filename>
<ogr:directory>/home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/custom/photos</ogr:directory>
<ogr:altitude>422.191</ogr:altitude>
<ogr:direction>59.9153</ogr:direction>
<ogr:longitude>149.2751666666667</ogr:longitude>
<ogr:latitude>-37.2305</ogr:latitude>
<ogr:timestamp>2012/03/06 14:28:40</ogr:timestamp>
</ogr:import_photos>
</gml:featureMember>
<gml:featureMember>
<ogr:import_photos fid="import_photos.1">
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>149.131877777778,-36.2208916666667,867</gml:coordinates></gml:Point></ogr:geometryProperty>
<ogr:photo>/home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/custom/photos/geotagged.jpg</ogr:photo>
<ogr:filename>geotagged</ogr:filename>
<ogr:directory>/home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/custom/photos</ogr:directory>
<ogr:altitude>867</ogr:altitude>
<ogr:longitude>149.1318777777778</ogr:longitude>
<ogr:latitude>-36.22089166666667</ogr:latitude>
<ogr:timestamp>2017/12/27 19:20:52</ogr:timestamp>
</ogr:import_photos>
</gml:featureMember>
</ogr:FeatureCollection>
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
<xs:complexType name="FeatureCollectionType">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureCollectionType">
<xs:attribute name="lockId" type="xs:string" use="optional"/>
<xs:attribute name="scope" type="xs:string" use="optional"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="import_photos" type="ogr:import_photos_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="import_photos_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="geometryProperty" type="gml:PointPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
<xs:element name="photo" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="255"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="filename" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="255"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="directory" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="255"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="altitude" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="direction" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="longitude" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="255"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="latitude" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="255"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="timestamp" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ import_photos_invalid.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>

<gml:featureMember>
<ogr:import_photos_invalid fid="import_photos_invalid.0">
<ogr:photo>/home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/custom/photos/not_photo.jpg</ogr:photo>
<ogr:filename>not_photo</ogr:filename>
<ogr:directory>/home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/custom/photos</ogr:directory>
<ogr:readable>false</ogr:readable>
</ogr:import_photos_invalid>
</gml:featureMember>
<gml:featureMember>
<ogr:import_photos_invalid fid="import_photos_invalid.1">
<ogr:photo>/home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/custom/photos/notags.JPG</ogr:photo>
<ogr:filename>notags</ogr:filename>
<ogr:directory>/home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/custom/photos</ogr:directory>
<ogr:readable>true</ogr:readable>
</ogr:import_photos_invalid>
</gml:featureMember>
</ogr:FeatureCollection>
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
<xs:complexType name="FeatureCollectionType">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureCollectionType">
<xs:attribute name="lockId" type="xs:string" use="optional"/>
<xs:attribute name="scope" type="xs:string" use="optional"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="import_photos_invalid" type="ogr:import_photos_invalid_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="import_photos_invalid_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="photo" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="255"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="filename" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="255"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="directory" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="255"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="readable" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:boolean">
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
@@ -4892,3 +4892,18 @@ tests:
OUTPUT:
name: expected/rotate_around_point.gml
type: vector

- algorithm: native:importphotos
name: Import photos
params:
FOLDER:
name: custom/photos
type: file
RECURSIVE: false
results:
INVALID:
name: expected/import_photos_invalid.gml
type: vector
OUTPUT:
name: expected/import_photos.gml
type: vector
@@ -40,6 +40,7 @@ SET(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmfixgeometries.cpp
processing/qgsalgorithmjoinbyattribute.cpp
processing/qgsalgorithmjoinwithlines.cpp
processing/qgsalgorithmimportphotos.cpp
processing/qgsalgorithmlineintersection.cpp
processing/qgsalgorithmloadlayer.cpp
processing/qgsalgorithmmeancoordinates.cpp
@@ -202,6 +203,8 @@ QT5_WRAP_CPP(QGIS_ANALYSIS_MOC_SRCS ${QGIS_ANALYSIS_MOC_HDRS})
# install headers

SET(QGIS_ANALYSIS_HDRS
processing/qgsalgorithmimportphotos.h

raster/qgsalignraster.h
raster/qgsaspectfilter.h
raster/qgsderivativefilter.h

0 comments on commit fa2c5ae

Please sign in to comment.
You can’t perform that action at this time.