Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[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 16, 2018
1 parent 87e8432 commit fa2c5ae
Show file tree
Hide file tree
Showing 20 changed files with 832 additions and 23 deletions.
1 change: 0 additions & 1 deletion python/core/raster/qgsrasterdataprovider.sip.in
Expand Up @@ -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);
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -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>
15 changes: 15 additions & 0 deletions python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
Expand Up @@ -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
3 changes: 3 additions & 0 deletions src/analysis/CMakeLists.txt
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit fa2c5ae

Please sign in to comment.