1"""
2@package iscatt.core_c
3
4@brief Wrappers for scatter plot C backend.
5
6(C) 2013 by the GRASS Development Team
7
8This program is free software under the GNU General Public License
9(>=v2). Read the file COPYING that comes with GRASS for details.
10
11@author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
12"""
13
14import sys
15import six
16import numpy as np
17from multiprocessing import Process, Queue
18
19from ctypes import *
20try:
21    from grass.lib.imagery import *
22    from grass.lib.gis import Cell_head, G_get_window
23except ImportError as e:
24    sys.stderr.write(_("Loading ctypes libs failed"))
25
26from core.gcmd import GException
27from grass.script import encode
28
29
30def Rasterize(polygon, rast, region, value):
31    rows, cols = rast.shape
32
33    # TODO creating of region is on many places
34    region['rows'] = rows
35    region['cols'] = cols
36
37    region['nsres'] = 1.0
38    region['ewres'] = 1.0
39
40    q = Queue()
41    p = Process(target=_rasterize, args=(polygon, rast, region, value, q))
42    p.start()
43    rast = q.get()
44    p.join()
45
46    return rast
47
48
49def ApplyColormap(vals, vals_mask, colmap, out_vals):
50
51    c_uint8_p = POINTER(c_uint8)
52
53    vals_p = vals.ctypes.data_as(c_uint8_p)
54
55    if hasattr(vals_mask, "ctypes"):
56        vals_mask_p = vals_mask.ctypes.data_as(c_uint8_p)
57    else:  # vals mask is empty (all data are selected)
58        vals_mask_p = None
59    colmap_p = colmap.ctypes.data_as(c_uint8_p)
60    out_vals_p = out_vals.ctypes.data_as(c_uint8_p)
61
62    vals_size = vals.reshape((-1)).shape[0]
63    I_apply_colormap(vals_p, vals_mask_p, vals_size, colmap_p, out_vals_p)
64
65
66def MergeArrays(merged_arr, overlay_arr, alpha):
67    if merged_arr.shape != overlay_arr.shape:
68        GException("MergeArrays: merged_arr.shape != overlay_arr.shape")
69
70    c_uint8_p = POINTER(c_uint8)
71    merged_p = merged_arr.ctypes.data_as(c_uint8_p)
72    overlay_p = overlay_arr.ctypes.data_as(c_uint8_p)
73
74    I_merge_arrays(
75        merged_p,
76        overlay_p,
77        merged_arr.shape[0],
78        merged_arr.shape[1],
79        alpha)
80
81
82def ComputeScatts(region, scatt_conds, bands, n_bands,
83                  scatts, cats_rasts_conds, cats_rasts):
84    _memmapToFileNames(scatts)
85    _memmapToFileNames(scatt_conds)
86
87    q = Queue()
88    p = Process(
89        target=_computeScattsProcess,
90        args=(
91            region,
92            scatt_conds,
93            bands,
94            n_bands,
95            scatts,
96            cats_rasts_conds,
97            cats_rasts,
98            q))
99    p.start()
100    ret = q.get()
101    p.join()
102
103    return ret[0], ret[1]
104
105#_memmapToFileNames and _fileNamesToMemmap are  workaround for older numpy version,
106# where memmap objects are not pickable,
107# and therefore cannot be passed to process spawned by multiprocessing module
108
109
110def _memmapToFileNames(data):
111
112    for k, v in six.iteritems(data):
113        if 'np_vals' in v:
114            data[k]['np_vals'] = v['np_vals'].filename()
115
116
117def _fileNamesToMemmap(data):
118    for k, v in six.iteritems(data):
119        if 'np_vals' in v:
120            data[k]['np_vals'] = np.memmap(filename=v['np_vals'])
121
122
123def UpdateCatRast(patch_rast, region, cat_rast):
124    q = Queue()
125    p = Process(
126        target=_updateCatRastProcess,
127        args=(
128            patch_rast,
129            region,
130            cat_rast,
131            q))
132    p.start()
133    ret = q.get()
134    p.join()
135
136    return ret
137
138
139def CreateCatRast(region, cat_rast):
140    cell_head = _regionToCellHead(region)
141    I_create_cat_rast(pointer(cell_head), cat_rast)
142
143
144def _computeScattsProcess(region, scatt_conds, bands, n_bands, scatts,
145                          cats_rasts_conds, cats_rasts, output_queue):
146
147    _fileNamesToMemmap(scatts)
148    _fileNamesToMemmap(scatt_conds)
149
150    sccats_c, cats_rasts_c, refs = _getComputationStruct(
151        scatts, cats_rasts, SC_SCATT_DATA, n_bands)
152    scatt_conds_c, cats_rasts_conds_c, refs2 = _getComputationStruct(
153        scatt_conds, cats_rasts_conds, SC_SCATT_CONDITIONS, n_bands)
154
155    char_bands = _stringListToCharArr(bands)
156
157    cell_head = _regionToCellHead(region)
158
159    ret = I_compute_scatts(pointer(cell_head),
160                           pointer(scatt_conds_c),
161                           pointer(cats_rasts_conds_c),
162                           pointer(char_bands),
163                           n_bands,
164                           pointer(sccats_c),
165                           pointer(cats_rasts_c))
166
167    I_sc_free_cats(pointer(sccats_c))
168    I_sc_free_cats(pointer(scatt_conds_c))
169
170    output_queue.put((ret, scatts))
171
172
173def _getBandcRange(band_info):
174    band_c_range = struct_Range()
175
176    band_c_range.max = band_info['max']
177    band_c_range.min = band_info['min']
178
179    return band_c_range
180
181
182def _regionToCellHead(region):
183    cell_head = struct_Cell_head()
184    G_get_window(pointer(cell_head))
185
186    convert_dict = {'n': 'north', 'e': 'east',
187                    'w': 'west', 's': 'south',
188                    'nsres': 'ns_res',
189                    'ewres': 'ew_res'}
190
191    for k, v in six.iteritems(region):
192        if k in ["rows", "cols", "cells", "zone"]:  # zone added in r65224
193            v = int(v)
194        else:
195            v = float(v)
196
197        if k in convert_dict:
198            k = convert_dict[k]
199
200        setattr(cell_head, k, v)
201
202    return cell_head
203
204
205def _stringListToCharArr(str_list):
206
207    arr = c_char_p * len(str_list)
208    char_arr = arr()
209    for i, st in enumerate(str_list):
210        if st:
211            char_arr[i] = encode(st)
212        else:
213            char_arr[i] = None
214
215    return char_arr
216
217
218def _getComputationStruct(cats, cats_rasts, cats_type, n_bands):
219
220    sccats = struct_scCats()
221    I_sc_init_cats(pointer(sccats), c_int(n_bands), c_int(cats_type))
222
223    refs = []
224    cats_rasts_core = []
225
226    for cat_id, scatt_ids in six.iteritems(cats):
227        cat_c_id = I_sc_add_cat(pointer(sccats))
228        cats_rasts_core.append(cats_rasts[cat_id])
229
230        for scatt_id, dt in six.iteritems(scatt_ids):
231            # if key is missing condition is always True (full scatter plor is
232            # computed)
233            vals = dt['np_vals']
234
235            scatt_vals = scdScattData()
236
237            c_void_p = ctypes.POINTER(ctypes.c_void_p)
238
239            if cats_type == SC_SCATT_DATA:
240                vals[:] = 0
241            elif cats_type == SC_SCATT_CONDITIONS:
242                pass
243            else:
244                return None
245            data_p = vals.ctypes.data_as(c_void_p)
246            I_scd_init_scatt_data(
247                pointer(scatt_vals),
248                cats_type, len(vals),
249                data_p)
250
251            refs.append(scatt_vals)
252
253            I_sc_insert_scatt_data(pointer(sccats),
254                                   pointer(scatt_vals),
255                                   cat_c_id, scatt_id)
256
257    cats_rasts_c = _stringListToCharArr(cats_rasts_core)
258
259    return sccats, cats_rasts_c, refs
260
261
262def _updateCatRastProcess(patch_rast, region, cat_rast, output_queue):
263    cell_head = _regionToCellHead(region)
264
265    ret = I_insert_patch_to_cat_rast(patch_rast,
266                                     pointer(cell_head),
267                                     cat_rast)
268
269    output_queue.put(ret)
270
271
272def _rasterize(polygon, rast, region, value, output_queue):
273    pol_size = len(polygon) * 2
274    pol = np.array(polygon, dtype=float)
275
276    c_uint8_p = POINTER(c_uint8)
277    c_double_p = POINTER(c_double)
278
279    pol_p = pol.ctypes.data_as(c_double_p)
280    rast_p = rast.ctypes.data_as(c_uint8_p)
281
282    cell_h = _regionToCellHead(region)
283    I_rasterize(pol_p,
284                len(polygon),
285                value,
286                pointer(cell_h), rast_p)
287
288    output_queue.put(rast)
289