Projet

Général

Profil

0002-workflows-add-action-to-geolocate-a-formdata-10581.patch

Frédéric Péters, 30 avril 2016 12:12

Télécharger (7,76 ko)

Voir les différences:

Subject: [PATCH 2/4] workflows: add action to geolocate a formdata (#10581)

 wcs/wf/geolocate.py | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 wcs/workflows.py    |   1 +
 2 files changed, 173 insertions(+)
 create mode 100644 wcs/wf/geolocate.py
wcs/wf/geolocate.py
1
# w.c.s. - web application for online forms
2
# Copyright (C) 2005-2016  Entr'ouvert
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, see <http://www.gnu.org/licenses/>.
16

  
17
import collections
18
import json
19
import urllib2
20

  
21
try:
22
    from PIL import Image
23
    from PIL.ExifTags import TAGS, GPSTAGS
24
except ImportError:
25
    Image = None
26

  
27
from quixote import get_publisher
28

  
29
from qommon import get_logger
30
from qommon.form import RadiobuttonsWidget, StringWidget, CheckboxWidget
31
from qommon.misc import http_get_page
32
from wcs.workflows import (WorkflowStatusItem, register_item_class,
33
        template_on_formdata)
34

  
35
class GeolocateWorkflowStatusItem(WorkflowStatusItem):
36
    description = N_('Geolocate')
37
    key = 'geolocate'
38

  
39
    method = 'string'
40
    address_string = None
41
    map_variable = None
42
    photo_variable = None
43
    overwrite = False
44

  
45
    def get_parameters(self):
46
        return ('method', 'address_string', 'map_variable', 'photo_variable', 'overwrite')
47

  
48
    def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
49
        methods = collections.OrderedDict(
50
                [('address_string', _('Address String')),
51
                 ('map_variable', _('Map Variable')),
52
                 ('photo_variable', _('Photo Variable'))])
53

  
54
        if Image is None:
55
            del methods['photo_variable']
56

  
57
        if 'method' in parameters:
58
            form.add(RadiobuttonsWidget, '%smethod' % prefix,
59
                    title=_('Method'),
60
                    options=methods.items(),
61
                    value=self.method,
62
                    attrs={'data-dynamic-display-parent': 'true'})
63
        if 'address_string' in parameters:
64
            form.add(StringWidget, '%saddress_string' % prefix,
65
                    title=_('Address String'), value=self.address_string,
66
                    attrs={
67
                        'data-dynamic-display-child-of': '%smethod' % prefix,
68
                        'data-dynamic-display-value': methods.get('address_string'),
69
                    })
70
        if 'map_variable' in parameters:
71
            form.add(StringWidget, '%smap_variable' % prefix,
72
                    title=_('Map Variable'), value=self.map_variable,
73
                    attrs={
74
                        'data-dynamic-display-child-of': '%smethod' % prefix,
75
                        'data-dynamic-display-value': methods.get('map_variable'),
76
                    })
77
        if 'photo_variable' in parameters:
78
            form.add(StringWidget, '%sphoto_variable' % prefix,
79
                    title=_('Photo Variable'), value=self.photo_variable,
80
                    attrs={
81
                        'data-dynamic-display-child-of': '%smethod' % prefix,
82
                        'data-dynamic-display-value': methods.get('photo_variable'),
83
                    })
84
        if 'overwrite' in parameters:
85
            form.add(CheckboxWidget, '%soverwrite' % prefix,
86
                    title=_('Overwrite existing geolocation'),
87
                    value=self.overwrite)
88

  
89
    def perform(self, formdata):
90
        if not self.method:
91
            return
92
        geolocation_point = formdata.formdef.geolocations.keys()[0]
93
        if not formdata.geolocations:
94
            formdata.geolocations = {}
95
        if formdata.geolocations.get(geolocation_point) and not self.overwrite:
96
            return
97
        location = getattr(self, 'geolocate_' + self.method)(formdata)
98
        if location:
99
            formdata.geolocations[geolocation_point] = location
100
            formdata.store()
101

  
102
    def geolocate_address_string(self, formdata):
103
        nominatim_url = get_publisher().get_site_option('nominatim_url')
104
        if not nominatim_url:
105
            nominatim_url = 'http://nominatim.openstreetmap.org'
106

  
107
        try:
108
            address = template_on_formdata(formdata, self.address_string)
109
        except ezt.EZTException:
110
            get_logger().error('error in template for address string [%s]', self.address_string)
111
            return
112

  
113
        url = '%s/search?q=%s&format=json' % (nominatim_url, urllib2.quote(address))
114
        response, status, data, auth_header = http_get_page(url)
115
        if status != 200:
116
            get_logger().error('error calling geocoding service [%s]', status)
117
            return
118
        data = json.loads(data)
119
        if len(data) == 0:
120
            get_logger().error('error finding location')
121
            return
122
        coords = data[0]
123
        return {'lon': float(coords['lon']), 'lat': float(coords['lat'])}
124

  
125
    def geolocate_map_variable(self, formdata):
126
        try:
127
            value = self.compute(self.map_variable)
128
        except:
129
            get_logger().error('error geolocating from map variable [%s]', self.map_variable)
130
            return
131
        if not value:
132
            return
133

  
134
        lat, lon = map(float, value.split(';'))
135
        return {'lon': lon, 'lat': lat}
136

  
137
    def geolocate_photo_variable(self, formdata):
138
        if Image is None:
139
            get_logger().error('error geolocating from file (missing PIL)')
140
            return
141

  
142
        try:
143
            value = self.compute(self.photo_variable)
144
        except:
145
            get_logger().error('error geolocating from photo variable [%s]', self.photo_variable)
146
            return
147

  
148
        try:
149
            image = Image.open(value.get_file_pointer())
150
        except IOError:
151
            get_logger().error('error geolocating from file')
152
            return
153

  
154
        exif_data = image._getexif()
155
        if exif_data:
156
            gps_info = exif_data.get(0x8825)
157
            if gps_info:
158
                # lat_ref will be N/S, lon_ref wil l be E/W
159
                # lat and lon will be degrees/minutes/seconds (value, denominator),
160
                # like ((33, 1), (51, 1), (2191, 100))
161
                lat_ref, lat, lon_ref, lon = gps_info[1], gps_info[2], gps_info[3], gps_info[4]
162
                lat = (1.0*lat[0][0]/lat[0][1] + 1.0*lat[1][0]/lat[1][1]/60 + 1.0*lat[2][0]/lat[2][1]/3600)
163
                lon = (1.0*lon[0][0]/lon[0][1] + 1.0*lon[1][0]/lon[1][1]/60 + 1.0*lon[2][0]/lon[2][1]/3600)
164
                if lat_ref == 'S':
165
                    lat = -lat
166
                if lon_ref == 'W':
167
                    lon = -lon
168
                return {'lon': lon, 'lat': lat}
169
        return
170

  
171

  
172
register_item_class(GeolocateWorkflowStatusItem)
wcs/workflows.py
2204 2204
    import wf.remove
2205 2205
    import wf.roles
2206 2206
    import wf.dispatch
2207
    import wf.geolocate
2207 2208
    import wf.wscall
2208 2209
    import wf.form
2209 2210
    import wf.register_comment
2210
-