pygmi 2.2.7__zip

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. pygmi-2.2.7/LICENSE.txt +623 -0
  2. pygmi-2.2.7/MANIFEST.in +7 -0
  3. pygmi-2.2.7/PKG-INFO +47 -0
  4. pygmi-2.2.7/README.md +55 -0
  5. pygmi-2.2.7/README.rst +20 -0
  6. pygmi-2.2.7/log.txt +242 -0
  7. pygmi-2.2.7/pygmi/__init__.py +22 -0
  8. pygmi-2.2.7/pygmi/clust/__init__.py +4 -0
  9. pygmi-2.2.7/pygmi/clust/crisp_clust.py +708 -0
  10. pygmi-2.2.7/pygmi/clust/datatypes.py +65 -0
  11. pygmi-2.2.7/pygmi/clust/fuzzy_clust.py +906 -0
  12. pygmi-2.2.7/pygmi/clust/graphs.py +421 -0
  13. pygmi-2.2.7/pygmi/clust/graphtool.py +667 -0
  14. pygmi-2.2.7/pygmi/clust/menu.py +139 -0
  15. pygmi-2.2.7/pygmi/clust/var_ratio.py +92 -0
  16. pygmi-2.2.7/pygmi/clust/xie_beni.py +93 -0
  17. pygmi-2.2.7/pygmi/helpdocs/linepointer.png +0 -0
  18. pygmi-2.2.7/pygmi/helpdocs/pygmi.main.html +31 -0
  19. pygmi-2.2.7/pygmi/helpdocs/pygmi.pfmod.ddisp.html +15 -0
  20. pygmi-2.2.7/pygmi/helpdocs/pygmi.pfmod.iodefs.exportkmz.html +13 -0
  21. pygmi-2.2.7/pygmi/helpdocs/pygmi.pfmod.iodefs.importpicture.html +16 -0
  22. pygmi-2.2.7/pygmi/helpdocs/pygmi.pfmod.layer.html +17 -0
  23. pygmi-2.2.7/pygmi/helpdocs/pygmi.pfmod.mext.html +44 -0
  24. pygmi-2.2.7/pygmi/helpdocs/pygmi.pfmod.misc.rangedcopy.html +16 -0
  25. pygmi-2.2.7/pygmi/helpdocs/pygmi.pfmod.param.html +37 -0
  26. pygmi-2.2.7/pygmi/helpdocs/pygmi.pfmod.prof.html +32 -0
  27. pygmi-2.2.7/pygmi/helpdocs/pygmi.pfmod.pview.html +26 -0
  28. pygmi-2.2.7/pygmi/helpdocs/pygmi.raster.cooper.gradients.html +14 -0
  29. pygmi-2.2.7/pygmi/helpdocs/pygmi.raster.cooper.tilt.html +14 -0
  30. pygmi-2.2.7/pygmi/helpdocs/pygmi.raster.cooper.visibility.html +14 -0
  31. pygmi-2.2.7/pygmi/helpdocs/pygmi.raster.dataprep.datagrid.html +13 -0
  32. pygmi-2.2.7/pygmi/helpdocs/pygmi.raster.dataprep.datamerge.html +11 -0
  33. pygmi-2.2.7/pygmi/helpdocs/pygmi.raster.dataprep.datareproj.html +11 -0
  34. pygmi-2.2.7/pygmi/helpdocs/pygmi.raster.dataprep.rtp.html +14 -0
  35. pygmi-2.2.7/pygmi/helpdocs/pygmi.raster.ginterp.html +31 -0
  36. pygmi-2.2.7/pygmi/helpdocs/pygmi.raster.igrf.html +20 -0
  37. pygmi-2.2.7/pygmi/helpdocs/pygmi.raster.normalisation.html +14 -0
  38. pygmi-2.2.7/pygmi/helpdocs/pygmi.raster.smooth.html +22 -0
  39. pygmi-2.2.7/pygmi/helpdocs/pygmi.raster.tiltdepth.html +20 -0
  40. pygmi-2.2.7/pygmi/images/Help.png +0 -0
  41. pygmi-2.2.7/pygmi/images/bringtofront.png +0 -0
  42. pygmi-2.2.7/pygmi/images/cgs2.emf +0 -0
  43. pygmi-2.2.7/pygmi/images/delete.png +0 -0
  44. pygmi-2.2.7/pygmi/images/linepointer.png +0 -0
  45. pygmi-2.2.7/pygmi/images/logo256.ico +0 -0
  46. pygmi-2.2.7/pygmi/images/logo256.png +0 -0
  47. pygmi-2.2.7/pygmi/images/pointer.png +0 -0
  48. pygmi-2.2.7/pygmi/images/pygmi.png +0 -0
  49. pygmi-2.2.7/pygmi/images/sendtoback.png +0 -0
  50. pygmi-2.2.7/pygmi/main.py +1037 -0
  51. pygmi-2.2.7/pygmi/menu_default.py +327 -0
  52. pygmi-2.2.7/pygmi/misc.py +147 -0
  53. pygmi-2.2.7/pygmi/pfmod/__init__.py +6 -0
  54. pygmi-2.2.7/pygmi/pfmod/cubes.py +1488 -0
  55. pygmi-2.2.7/pygmi/pfmod/datatypes.py +292 -0
  56. pygmi-2.2.7/pygmi/pfmod/grvmag3d.py +1265 -0
  57. pygmi-2.2.7/pygmi/pfmod/iodefs.py +1001 -0
  58. pygmi-2.2.7/pygmi/pfmod/menu.py +107 -0
  59. pygmi-2.2.7/pygmi/pfmod/misc.py +301 -0
  60. pygmi-2.2.7/pygmi/pfmod/pfmod.py +248 -0
  61. pygmi-2.2.7/pygmi/pfmod/tab_ddisp.py +425 -0
  62. pygmi-2.2.7/pygmi/pfmod/tab_layer.py +490 -0
  63. pygmi-2.2.7/pygmi/pfmod/tab_mext.py +544 -0
  64. pygmi-2.2.7/pygmi/pfmod/tab_param.py +548 -0
  65. pygmi-2.2.7/pygmi/pfmod/tab_prof.py +946 -0
  66. pygmi-2.2.7/pygmi/pfmod/tab_pview.py +740 -0
  67. pygmi-2.2.7/pygmi/raster/IGRF11.COF +1635 -0
  68. pygmi-2.2.7/pygmi/raster/__init__.py +14 -0
  69. pygmi-2.2.7/pygmi/raster/cooper.py +718 -0
  70. pygmi-2.2.7/pygmi/raster/dataprep.py +1640 -0
  71. pygmi-2.2.7/pygmi/raster/datatypes.py +123 -0
  72. pygmi-2.2.7/pygmi/raster/equation_editor.py +256 -0
  73. pygmi-2.2.7/pygmi/raster/ginterp.py +1901 -0
  74. pygmi-2.2.7/pygmi/raster/graphs.py +531 -0
  75. pygmi-2.2.7/pygmi/raster/igrf.py +860 -0
  76. pygmi-2.2.7/pygmi/raster/iodefs.py +813 -0
  77. pygmi-2.2.7/pygmi/raster/menu.py +307 -0
  78. pygmi-2.2.7/pygmi/raster/normalisation.py +187 -0
  79. pygmi-2.2.7/pygmi/raster/show_table.py +245 -0
  80. pygmi-2.2.7/pygmi/raster/smooth.py +406 -0
  81. pygmi-2.2.7/pygmi/raster/tiltdepth.py +590 -0
  82. pygmi-2.2.7/pygmi/seis/__init__.py +0 -0
  83. pygmi-2.2.7/pygmi/seis/beachball.py +751 -0
  84. pygmi-2.2.7/pygmi/seis/datatypes.py +605 -0
  85. pygmi-2.2.7/pygmi/seis/del_rec.py +410 -0
  86. pygmi-2.2.7/pygmi/seis/iodefs.py +1261 -0
  87. pygmi-2.2.7/pygmi/seis/menu.py +136 -0
  88. pygmi-2.2.7/pygmi/seis/scan_imp.py +795 -0
  89. pygmi-2.2.7/pygmi/test/__init__.py +6 -0
  90. pygmi-2.2.7/pygmi/test/pfmod.py +212 -0
  91. pygmi-2.2.7/pygmi/test/tests.py +39 -0
  92. pygmi-2.2.7/pygmi/vector/__init__.py +7 -0
  93. pygmi-2.2.7/pygmi/vector/datatypes.py +74 -0
  94. pygmi-2.2.7/pygmi/vector/graphs.py +443 -0
  95. pygmi-2.2.7/pygmi/vector/iodefs.py +345 -0
  96. pygmi-2.2.7/pygmi/vector/menu.py +127 -0
  97. pygmi-2.2.7/pygmi.egg-info/PKG-INFO +47 -0
  98. pygmi-2.2.7/pygmi.egg-info/SOURCES.txt +105 -0
  99. pygmi-2.2.7/pygmi.egg-info/dependency_links.txt +1 -0
  100. pygmi-2.2.7/pygmi.egg-info/entry_points.txt +3 -0
  101. pygmi-2.2.7/pygmi.egg-info/not-zip-safe +1 -0
  102. pygmi-2.2.7/pygmi.egg-info/requires.txt +7 -0
  103. pygmi-2.2.7/pygmi.egg-info/top_level.txt +1 -0
  104. pygmi-2.2.7/quickstart.py +35 -0
  105. pygmi-2.2.7/setup.cfg +8 -0
  106. pygmi-2.2.7/setup.py +80 -0
@@ -0,0 +1,1640 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Name: dataprep.py (part of PyGMI)
3
+ #
4
+ # Author: Patrick Cole
5
+ # E-Mail: pcole@geoscience.org.za
6
+ #
7
+ # Copyright: (c) 2013 Council for Geoscience
8
+ # Licence: GPL-3.0
9
+ #
10
+ # This file is part of PyGMI
11
+ #
12
+ # PyGMI is free software: you can redistribute it and/or modify
13
+ # it under the terms of the GNU General Public License as published by
14
+ # the Free Software Foundation, either version 3 of the License, or
15
+ # (at your option) any later version.
16
+ #
17
+ # PyGMI is distributed in the hope that it will be useful,
18
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ # GNU General Public License for more details.
21
+ #
22
+ # You should have received a copy of the GNU General Public License
23
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
24
+ # -----------------------------------------------------------------------------
25
+ """ This is a set of Raster Data Preparation routines """
26
+
27
+ from __future__ import print_function
28
+
29
+ from PyQt4 import QtGui, QtCore
30
+ import os
31
+ import numpy as np
32
+ from osgeo import gdal, osr, ogr
33
+ from pygmi.raster.datatypes import Data
34
+ from pygmi.vector.datatypes import PData
35
+ from PIL import Image, ImageDraw
36
+ import copy
37
+ import scipy.ndimage as ndimage
38
+ from collections import Counter
39
+ import pygmi.menu_default as menu_default
40
+
41
+ gdal.PushErrorHandler('CPLQuietErrorHandler')
42
+
43
+
44
+ class DataCut(object):
45
+ """
46
+ Cut Data using shapefiles
47
+
48
+ This class cuts raster datasets using a boundary defined by a polygon
49
+ shapefile.
50
+
51
+ Attributes
52
+ ----------
53
+ ifile : str
54
+ input file name.
55
+ name : str
56
+ item name
57
+ ext : str
58
+ file name extension.
59
+ pbar : progressbar
60
+ reference to a progress bar.
61
+ parent : parent
62
+ reference to the parent routine
63
+ indata : dictionary
64
+ dictionary of input datasets
65
+ outdata : dictionary
66
+ dictionary of output datasets
67
+ """
68
+ def __init__(self, parent):
69
+ self.ifile = ""
70
+ self.name = "Cut Data:"
71
+ self.ext = ""
72
+ self.pbar = parent.pbar
73
+ self.parent = parent
74
+ self.indata = {}
75
+ self.outdata = {}
76
+ # self.dirname = ""
77
+
78
+ def settings(self):
79
+ """ Show Info """
80
+ # if 'Cluster' in self.indata:
81
+ # data = self.indata['Cluster']
82
+ if 'Raster' in self.indata:
83
+ data = copy.deepcopy(self.indata['Raster'])
84
+ else:
85
+ self.parent.showprocesslog('No raster data')
86
+ return
87
+
88
+ ext = "Shape file (*.shp)"
89
+
90
+ filename = QtGui.QFileDialog.getOpenFileName(
91
+ self.parent, 'Open Shape File', '.', ext)
92
+ if filename == '':
93
+ return False
94
+ os.chdir(filename.rpartition('/')[0])
95
+
96
+ self.ifile = str(filename)
97
+ self.ext = filename[-3:]
98
+ data = cut_raster(data, self.ifile)
99
+
100
+ if data is None:
101
+ err = ('There was a problem importing the shapefile. Please make '
102
+ 'sure you have at all the individual files which make up '
103
+ 'the shapefile.')
104
+ QtGui.QMessageBox.warning(self.parent, 'Error', err,
105
+ QtGui.QMessageBox.Ok,
106
+ QtGui.QMessageBox.Ok)
107
+ return False
108
+
109
+
110
+ # data = trim_raster(data)
111
+ self.pbar.to_max()
112
+ self.outdata['Raster'] = data
113
+
114
+ return True
115
+
116
+
117
+ class DataGrid(QtGui.QDialog):
118
+ """
119
+ Grid Point Data
120
+
121
+ This class grids point data using a nearest neighbourhood technique.
122
+
123
+ Attributes
124
+ ----------
125
+ parent : parent
126
+ reference to the parent routine
127
+ indata : dictionary
128
+ dictionary of input datasets
129
+ outdata : dictionary
130
+ dictionary of output datasets
131
+ """
132
+ def __init__(self, parent=None):
133
+ QtGui.QDialog.__init__(self, parent)
134
+
135
+ self.indata = {}
136
+ self.outdata = {}
137
+ self.parent = parent
138
+ self.pbar = parent.pbar
139
+
140
+ self.dsb_dxy = QtGui.QDoubleSpinBox()
141
+ self.dataid = QtGui.QComboBox()
142
+ self.label_rows = QtGui.QLabel()
143
+ self.label_cols = QtGui.QLabel()
144
+
145
+ self.setupui()
146
+
147
+ def setupui(self):
148
+ """ Setup UI """
149
+ gridlayout_main = QtGui.QGridLayout(self)
150
+ buttonbox = QtGui.QDialogButtonBox()
151
+ helpdocs = menu_default.HelpButton('pygmi.raster.dataprep.datagrid')
152
+ label_band = QtGui.QLabel()
153
+ label_dxy = QtGui.QLabel()
154
+
155
+ self.dsb_dxy.setMaximum(9999999999.0)
156
+ self.dsb_dxy.setMinimum(0.00001)
157
+ self.dsb_dxy.setDecimals(5)
158
+ buttonbox.setOrientation(QtCore.Qt.Horizontal)
159
+ buttonbox.setCenterButtons(True)
160
+ buttonbox.setStandardButtons(buttonbox.Cancel | buttonbox.Ok)
161
+
162
+ self.setWindowTitle("Dataset Gridding")
163
+ self.label_rows.setText("Rows: 0")
164
+ self.label_cols.setText("Columns: 0")
165
+ label_dxy.setText("Cell Size:")
166
+ label_band.setText("Column to Grid:")
167
+
168
+ gridlayout_main.addWidget(label_dxy, 0, 0, 1, 1)
169
+ gridlayout_main.addWidget(self.dsb_dxy, 0, 1, 1, 1)
170
+ gridlayout_main.addWidget(self.label_rows, 1, 0, 1, 2)
171
+ gridlayout_main.addWidget(self.label_cols, 2, 0, 1, 2)
172
+ gridlayout_main.addWidget(label_band, 3, 0, 1, 1)
173
+ gridlayout_main.addWidget(self.dataid, 3, 1, 1, 1)
174
+ gridlayout_main.addWidget(helpdocs, 4, 0, 1, 1)
175
+ gridlayout_main.addWidget(buttonbox, 4, 1, 1, 3)
176
+
177
+ buttonbox.accepted.connect(self.accept)
178
+ buttonbox.rejected.connect(self.reject)
179
+ self.dsb_dxy.valueChanged.connect(self.dxy_change)
180
+
181
+ def dxy_change(self):
182
+ """ update dxy """
183
+ dxy = self.dsb_dxy.value()
184
+ data = self.indata['Point'][0]
185
+ x = data.xdata
186
+ y = data.ydata
187
+
188
+ cols = int(x.ptp()/dxy)
189
+ rows = int(y.ptp()/dxy)
190
+
191
+ self.label_rows.setText("Rows: "+str(rows))
192
+ self.label_cols.setText("Columns: "+str(cols))
193
+
194
+ def settings(self):
195
+ """ Settings """
196
+ tmp = []
197
+ if 'Point' not in self.indata:
198
+ return False
199
+
200
+ for i in self.indata['Point']:
201
+ tmp.append(i.dataid)
202
+
203
+ self.dataid.addItems(tmp)
204
+
205
+ data = self.indata['Point'][0]
206
+ x = data.xdata
207
+ y = data.ydata
208
+
209
+ dx = x.ptp()/np.sqrt(x.size)
210
+ dy = y.ptp()/np.sqrt(y.size)
211
+ dxy = max(dx, dy)
212
+
213
+ # xy = np.transpose([x, y])
214
+ # xy = xy.tolist()
215
+ # xy = np.array(xy)
216
+ # xy = xy[:-1]-xy[1:]
217
+ # dxy = np.median(np.sqrt(np.sum(xy**2, 1)))/3
218
+
219
+ self.dsb_dxy.setValue(dxy)
220
+ self.dxy_change()
221
+ tmp = self.exec_()
222
+
223
+ if tmp == 1:
224
+ self.acceptall()
225
+ tmp = True
226
+
227
+ return tmp
228
+
229
+ def acceptall(self):
230
+ """ accept """
231
+ dxy = self.dsb_dxy.value()
232
+ data = self.indata['Point'][0]
233
+
234
+ newdat = []
235
+ for data in self.pbar.iter(self.indata['Point']):
236
+ if data.dataid != self.dataid.currentText():
237
+ continue
238
+ x = data.xdata
239
+ y = data.ydata
240
+ z = data.zdata
241
+
242
+ for i in [x, y, z]:
243
+ filt = np.logical_not(np.isnan(i))
244
+ x = x[filt]
245
+ y = y[filt]
246
+ z = z[filt]
247
+
248
+ # self.parent.showprocesslog
249
+ # pdb.set_trace()
250
+ tmp = quickgrid(x, y, z, dxy, showtext=self.parent.showprocesslog)
251
+ # pdb.set_trace()
252
+ mask = tmp.mask
253
+ gdat = tmp.data
254
+
255
+ # Create dataset
256
+ dat = Data()
257
+ dat.data = np.ma.masked_invalid(gdat[::-1])
258
+ dat.data.mask = mask[::-1]
259
+ dat.rows, dat.cols = gdat.shape
260
+ dat.nullvalue = dat.data.fill_value
261
+ dat.dataid = data.dataid
262
+ dat.tlx = x.min()
263
+ dat.tly = y.max()
264
+ dat.xdim = dxy
265
+ dat.ydim = dxy
266
+ newdat.append(dat)
267
+
268
+ self.outdata['Raster'] = newdat
269
+ self.outdata['Point'] = self.indata['Point']
270
+
271
+
272
+ class DataMerge(QtGui.QDialog):
273
+ """
274
+ Data Merge
275
+
276
+ This class merges datasets which have different rows and columns. It
277
+ resamples them so that they have the same rows and columns.
278
+
279
+ Attributes
280
+ ----------
281
+ parent : parent
282
+ reference to the parent routine
283
+ indata : dictionary
284
+ dictionary of input datasets
285
+ outdata : dictionary
286
+ dictionary of output datasets
287
+ """
288
+ def __init__(self, parent=None):
289
+ QtGui.QDialog.__init__(self, parent)
290
+
291
+ self.indata = {}
292
+ self.outdata = {}
293
+ self.parent = parent
294
+ self.pbar = self.parent.pbar
295
+
296
+ self.dsb_dxy = QtGui.QDoubleSpinBox()
297
+ self.label_rows = QtGui.QLabel()
298
+ self.label_cols = QtGui.QLabel()
299
+
300
+ self.setupui()
301
+
302
+ def setupui(self):
303
+ """ Setup UI """
304
+
305
+ gridlayout_main = QtGui.QGridLayout(self)
306
+ buttonbox = QtGui.QDialogButtonBox()
307
+ helpdocs = menu_default.HelpButton('pygmi.raster.dataprep.datamerge')
308
+ label_dxy = QtGui.QLabel()
309
+
310
+ self.dsb_dxy.setMaximum(9999999999.0)
311
+ self.dsb_dxy.setMinimum(0.00001)
312
+ self.dsb_dxy.setDecimals(5)
313
+ buttonbox.setOrientation(QtCore.Qt.Horizontal)
314
+ buttonbox.setCenterButtons(True)
315
+ buttonbox.setStandardButtons(buttonbox.Cancel | buttonbox.Ok)
316
+
317
+ self.setWindowTitle("Dataset Merge and Resample")
318
+ self.label_rows.setText("Rows: 0")
319
+ self.label_cols.setText("Columns: 0")
320
+ label_dxy.setText("Cell Size:")
321
+
322
+ gridlayout_main.addWidget(label_dxy, 0, 0, 1, 1)
323
+ gridlayout_main.addWidget(self.dsb_dxy, 0, 1, 1, 1)
324
+ gridlayout_main.addWidget(self.label_rows, 1, 0, 1, 2)
325
+ gridlayout_main.addWidget(self.label_cols, 2, 0, 1, 2)
326
+ gridlayout_main.addWidget(helpdocs, 3, 0, 1, 1)
327
+ gridlayout_main.addWidget(buttonbox, 3, 1, 1, 1)
328
+
329
+ buttonbox.accepted.connect(self.accept)
330
+ buttonbox.rejected.connect(self.reject)
331
+ self.dsb_dxy.valueChanged.connect(self.dxy_change)
332
+
333
+ def dxy_change(self):
334
+ """ Update dxy - which is the size of a grid cell in the x and y
335
+ directions."""
336
+ data = self.indata['Raster'][0]
337
+ dxy = self.dsb_dxy.value()
338
+
339
+ xmin0 = data.tlx
340
+ xmax0 = data.tlx+data.xdim*data.cols
341
+ ymax0 = data.tly
342
+ ymin0 = data.tly-data.ydim*data.rows
343
+
344
+ for data in self.indata['Raster']:
345
+ xmin = min(data.tlx, xmin0)
346
+ xmax = max(data.tlx+data.xdim*data.cols, xmax0)
347
+ ymax = max(data.tly, ymax0)
348
+ ymin = min(data.tly-data.ydim*data.rows, ymin0)
349
+
350
+ cols = int((xmax - xmin)/dxy)
351
+ rows = int((ymax - ymin)/dxy)
352
+
353
+ self.label_rows.setText("Rows: "+str(rows))
354
+ self.label_cols.setText("Columns: "+str(cols))
355
+
356
+ def settings(self):
357
+ """ Settings """
358
+ data = self.indata['Raster'][0]
359
+ dxy0 = min(data.xdim, data.ydim)
360
+ for data in self.indata['Raster']:
361
+ dxy = min(dxy0, data.xdim, data.ydim)
362
+
363
+ self.dsb_dxy.setValue(dxy)
364
+ tmp = self.exec_()
365
+
366
+ if tmp == 1:
367
+ self.acceptall()
368
+ tmp = True
369
+
370
+ return tmp
371
+
372
+ def acceptall(self):
373
+ """
374
+ This routine is called by settings() if accept is pressed. It contains
375
+ the main merge routine.
376
+ """
377
+ dxy = self.dsb_dxy.value()
378
+ data = self.indata['Raster'][0]
379
+ orig_wkt = data.wkt
380
+
381
+ xmin = data.tlx
382
+ xmax = data.tlx+data.xdim*data.cols
383
+ ymax = data.tly
384
+ ymin = data.tly-data.ydim*data.rows
385
+
386
+ for data in self.indata['Raster']:
387
+ xmin = min(data.tlx, xmin)
388
+ xmax = max(data.tlx+data.xdim*data.cols, xmax)
389
+ ymax = max(data.tly, ymax)
390
+ ymin = min(data.tly-data.ydim*data.rows, ymin)
391
+
392
+ cols = int((xmax - xmin)/dxy)
393
+ rows = int((ymax - ymin)/dxy)
394
+ gtr = (xmin, dxy, 0.0, ymax, 0.0, -dxy)
395
+
396
+ if cols == 0 or rows == 0:
397
+ self.parent.showprocesslog("Your rows or cols are zero. " +
398
+ "Your input projection may be wrong")
399
+ return
400
+
401
+ dat = []
402
+ for data in self.pbar.iter(self.indata['Raster']):
403
+ doffset = 0.0
404
+ if data.data.min() <= 0:
405
+ doffset = data.data.min()-1.
406
+ data.data -= doffset
407
+ gtr0 = (data.tlx, data.xdim, 0.0, data.tly, 0.0, -data.ydim)
408
+ src = data_to_gdal_mem(data, gtr0, orig_wkt, data.cols, data.rows)
409
+ dest = data_to_gdal_mem(data, gtr, orig_wkt, cols, rows, True)
410
+
411
+ gdal.ReprojectImage(src, dest, orig_wkt, orig_wkt,
412
+ gdal.GRA_Bilinear)
413
+
414
+ dat.append(gdal_to_dat(dest, data.dataid))
415
+ dat[-1].data += doffset
416
+
417
+ # mask = dat[-1].data.mask
418
+ # for i in dat:
419
+ # mask += i.data.mask
420
+ # mask[mask != 0] = 1
421
+ # for i in dat:
422
+ # i.data.mask = mask
423
+
424
+ self.outdata['Raster'] = dat
425
+ # self.accept()
426
+
427
+
428
+ class DataReproj(QtGui.QDialog):
429
+ """
430
+ Reprojections
431
+
432
+ This class reprojects datasets using the GDAL routines.
433
+
434
+ Attributes
435
+ ----------
436
+ parent : parent
437
+ reference to the parent routine
438
+ indata : dictionary
439
+ dictionary of input datasets
440
+ outdata : dictionary
441
+ dictionary of output datasets
442
+ """
443
+ def __init__(self, parent=None):
444
+ QtGui.QDialog.__init__(self, parent)
445
+
446
+ self.indata = {}
447
+ self.outdata = {}
448
+ self.parent = parent
449
+ self.pbar = self.parent.pbar
450
+
451
+ self.groupboxb = QtGui.QGroupBox()
452
+ self.combobox_inp_epsg = QtGui.QComboBox()
453
+ self.inp_epsg_info = QtGui.QLabel()
454
+ self.groupbox2b = QtGui.QGroupBox()
455
+ self.combobox_out_epsg = QtGui.QComboBox()
456
+ self.out_epsg_info = QtGui.QLabel()
457
+ self.in_proj = GroupProj('Input Projection')
458
+ self.out_proj = GroupProj('Output Projection')
459
+
460
+ self.setupui()
461
+
462
+ def setupui(self):
463
+ """ Setup UI """
464
+ gridlayout_main = QtGui.QGridLayout(self)
465
+ buttonbox = QtGui.QDialogButtonBox()
466
+ helpdocs = menu_default.HelpButton('pygmi.raster.dataprep.datareproj')
467
+
468
+ buttonbox.setOrientation(QtCore.Qt.Horizontal)
469
+ buttonbox.setCenterButtons(True)
470
+ buttonbox.setStandardButtons(buttonbox.Cancel | buttonbox.Ok)
471
+
472
+ self.setWindowTitle("Dataset Reprojection")
473
+
474
+ gridlayout_main.addWidget(self.in_proj, 0, 0, 1, 1)
475
+ gridlayout_main.addWidget(self.out_proj, 0, 1, 1, 1)
476
+ gridlayout_main.addWidget(helpdocs, 1, 0, 1, 1)
477
+ gridlayout_main.addWidget(buttonbox, 1, 1, 1, 1)
478
+
479
+ buttonbox.accepted.connect(self.accept)
480
+ buttonbox.rejected.connect(self.reject)
481
+
482
+ def acceptall(self):
483
+ """
484
+ This routine is called by settings() if accept is pressed. It contains
485
+ the main routine.
486
+ """
487
+
488
+ # Input stuff
489
+ orig_wkt = self.in_proj.wkt
490
+
491
+ orig = osr.SpatialReference()
492
+ orig.ImportFromWkt(orig_wkt)
493
+
494
+ # Output stuff
495
+ targ_wkt = self.out_proj.wkt
496
+
497
+ targ = osr.SpatialReference()
498
+ targ.ImportFromWkt(targ_wkt)
499
+
500
+ # Set transformation
501
+ ctrans = osr.CoordinateTransformation(orig, targ)
502
+
503
+ # Now create virtual dataset
504
+ dat = []
505
+ for data in self.pbar.iter(self.indata['Raster']):
506
+ datamin = data.data.min()
507
+ if datamin <= 0:
508
+ data.data = data.data-(datamin-1)
509
+
510
+ # Work out the boundaries of the new dataset in the target projection
511
+ u_l = ctrans.TransformPoint(data.tlx, data.tly)
512
+ u_r = ctrans.TransformPoint(data.tlx+data.xdim*data.cols, data.tly)
513
+ l_l = ctrans.TransformPoint(data.tlx, data.tly-data.ydim*data.rows)
514
+ l_r = ctrans.TransformPoint(data.tlx+data.xdim*data.cols,
515
+ data.tly - data.ydim*data.rows)
516
+
517
+ lrx = l_r[0]
518
+ llx = l_l[0]
519
+ ulx = u_l[0]
520
+ urx = u_r[0]
521
+ lry = l_r[1]
522
+ lly = l_l[1]
523
+ uly = u_l[1]
524
+ ury = u_r[1]
525
+
526
+ minx = min(llx, ulx, urx, lrx)
527
+ maxx = max(llx, ulx, urx, lrx)
528
+ miny = min(lly, lry, ury, uly)
529
+ maxy = max(lly, lry, ury, uly)
530
+ newdimx = (maxx-minx)/data.cols
531
+ newdimy = (maxy-miny)/data.rows
532
+ newdim = min(newdimx, newdimy)
533
+ cols = round((maxx - minx)/newdim)
534
+ rows = round((maxy - miny)/newdim)
535
+
536
+ if cols == 0 or rows == 0:
537
+ self.parent.showprocesslog("Your rows or cols are zero. " +
538
+ "Your input projection may be " +
539
+ "wrong")
540
+ return
541
+
542
+ # top left x, w-e pixel size, rotation, top left y, rotation, n-s pixel size
543
+ old_geo = (data.tlx, data.xdim, 0, data.tly, 0, -data.ydim)
544
+ src = data_to_gdal_mem(data, old_geo, orig_wkt, data.cols,
545
+ data.rows)
546
+
547
+ new_geo = (minx, newdim, 0, maxy, 0, -newdim)
548
+ dest = data_to_gdal_mem(data, new_geo, targ_wkt, cols, rows, True)
549
+
550
+ gdal.ReprojectImage(src, dest, orig_wkt, targ_wkt,
551
+ gdal.GRA_Bilinear)
552
+
553
+ data2 = gdal_to_dat(dest, data.dataid)
554
+ if datamin <= 0:
555
+ data2.data = data2.data+(datamin-1)
556
+ data.data = data.data+(datamin-1)
557
+ # mask = data2.data.mask
558
+ data2.data = np.ma.masked_equal(data2.data.filled(data.nullvalue),
559
+ data.nullvalue)
560
+ # data2.data.mask = mask
561
+ # data2.data.set_fill_value(data.nullvalue)
562
+ data2.nullvalue = data.nullvalue
563
+ data2.data = np.ma.masked_invalid(data2.data)
564
+ data2.data = np.ma.masked_less(data2.data, data.data.min())
565
+ data2.data = np.ma.masked_greater(data2.data, data.data.max())
566
+
567
+ dat.append(data2)
568
+
569
+ self.outdata['Raster'] = dat
570
+
571
+ def settings(self):
572
+ """ Settings """
573
+
574
+ self.in_proj.set_current(self.indata['Raster'][0].wkt)
575
+ self.out_proj.set_current(self.indata['Raster'][0].wkt)
576
+
577
+ tmp = self.exec_()
578
+ if tmp == 1:
579
+ self.acceptall()
580
+ tmp = True
581
+
582
+ return tmp
583
+
584
+
585
+ class GetProf(object):
586
+ """
587
+ Get a Profile
588
+
589
+ This class extracts a profile from a raster dataset using a line shapefile.
590
+
591
+ Attributes
592
+ ----------
593
+ ifile : str
594
+ input file name.
595
+ name : str
596
+ item name
597
+ ext : str
598
+ file name extension.
599
+ pbar : progressbar
600
+ reference to a progress bar.
601
+ parent : parent
602
+ reference to the parent routine
603
+ indata : dictionary
604
+ dictionary of input datasets
605
+ outdata : dictionary
606
+ dictionary of output datasets
607
+ """
608
+ def __init__(self, parent):
609
+ self.ifile = ""
610
+ self.name = "Get Profile: "
611
+ self.ext = ""
612
+ self.pbar = parent.pbar
613
+ self.parent = parent
614
+ self.indata = {}
615
+ self.outdata = {}
616
+ # self.dirname = ""
617
+
618
+ def settings(self):
619
+ """ Show Info """
620
+ # if 'Cluster' in self.indata:
621
+ # data = self.indata['Cluster']
622
+ if 'Raster' in self.indata:
623
+ data = copy.deepcopy(self.indata['Raster'])
624
+ else:
625
+ self.parent.showprocesslog('No raster data')
626
+ return
627
+
628
+ ext = "Shape file (*.shp)"
629
+
630
+ filename = QtGui.QFileDialog.getOpenFileName(
631
+ self.parent, 'Open Shape File', '.', ext)
632
+ if filename == '':
633
+ return False
634
+ os.chdir(filename.rpartition('/')[0])
635
+
636
+ self.ifile = str(filename)
637
+ self.ext = filename[-3:]
638
+
639
+ shapef = ogr.Open(self.ifile)
640
+ if shapef is None:
641
+ err = ('There was a problem importing the shapefile. Please make '
642
+ 'sure you have at all the individual files which make up '
643
+ 'the shapefile.')
644
+ QtGui.QMessageBox.warning(self.parent, 'Error', err,
645
+ QtGui.QMessageBox.Ok,
646
+ QtGui.QMessageBox.Ok)
647
+ return False
648
+
649
+ lyr = shapef.GetLayer()
650
+ line = lyr.GetNextFeature()
651
+ if lyr.GetGeomType() is not ogr.wkbLineString:
652
+ self.parent.showprocesslog('You need lines in that shape file')
653
+ return
654
+
655
+ allpoints = []
656
+ for idata in self.pbar.iter(data):
657
+ tmp = line.GetGeometryRef()
658
+ points = tmp.GetPoints()
659
+
660
+ x_0, y_0 = points[0]
661
+ x_1, y_1 = points[1]
662
+
663
+ bly = idata.tly-idata.ydim*idata.rows
664
+ x_0 = (x_0-idata.tlx)/idata.xdim
665
+ x_1 = (x_1-idata.tlx)/idata.xdim
666
+ y_0 = (y_0-bly)/idata.ydim
667
+ y_1 = (y_1-bly)/idata.ydim
668
+ rcell = int(np.sqrt((x_1-x_0)**2+(y_1-y_0)**2))
669
+ # rdist = np.sqrt((idata.xdim*(x_1-x_0))**2 +
670
+ # (idata.ydim*(y_1-y_0))**2)
671
+
672
+ xxx = np.linspace(x_0, x_1, rcell, False)
673
+ yyy = np.linspace(y_0, y_1, rcell, False)
674
+
675
+ tmpprof = ndimage.map_coordinates(idata.data[::-1], [yyy, xxx],
676
+ order=1, cval=np.nan)
677
+ xxx = xxx[np.logical_not(np.isnan(tmpprof))]
678
+ yyy = yyy[np.logical_not(np.isnan(tmpprof))]
679
+ tmpprof = tmpprof[np.logical_not(np.isnan(tmpprof))]
680
+ xxx = xxx*idata.xdim+idata.tlx
681
+ yyy = yyy*idata.ydim+bly
682
+ # allpoints.append(np.array([xxx, yyy, tmpprof]))
683
+ allpoints.append(PData())
684
+ allpoints[-1].xdata = xxx
685
+ allpoints[-1].ydata = yyy
686
+ allpoints[-1].zdata = tmpprof
687
+ allpoints[-1].dataid = idata.dataid
688
+
689
+ self.outdata['Point'] = allpoints
690
+
691
+ return True
692
+
693
+
694
+ class GroupProj(QtGui.QWidget):
695
+ """
696
+ Group Proj
697
+
698
+ Custom widget
699
+ """
700
+ def __init__(self, title='Projection', parent=None):
701
+ QtGui.QWidget.__init__(self, parent)
702
+
703
+ self.wkt = ''
704
+
705
+ self.gridlayout = QtGui.QGridLayout(self)
706
+ self.groupbox = QtGui.QGroupBox()
707
+ self.combobox = QtGui.QComboBox()
708
+ self.label = QtGui.QLabel()
709
+
710
+ self.gridlayout.addWidget(self.groupbox, 1, 0, 1, 2)
711
+
712
+ self.groupbox.setTitle(title)
713
+ gridlayout = QtGui.QGridLayout(self.groupbox)
714
+ gridlayout.addWidget(self.combobox, 0, 0, 1, 1)
715
+ gridlayout.addWidget(self.label, 1, 0, 1, 1)
716
+
717
+ self.epsg_proj = getepsgcodes()
718
+ self.epsg_proj['Current'] = self.wkt
719
+ tmp = list(self.epsg_proj.keys())
720
+ tmp.sort(key=lambda c: c.lower())
721
+ tmp = ['Current']+tmp
722
+
723
+ self.combobox.addItems(tmp)
724
+ self.combobox.currentIndexChanged.connect(self.combo_change)
725
+
726
+ def set_current(self, wkt):
727
+ """ Sets new wkt for current """
728
+ self.wkt = wkt
729
+ self.epsg_proj['Current'] = self.wkt
730
+ self.combo_change()
731
+
732
+ def combo_change(self):
733
+ """ Change Combo """
734
+ indx = self.combobox.currentIndex()
735
+ txt = self.combobox.itemText(indx)
736
+
737
+ self.wkt = self.epsg_proj[txt]
738
+
739
+ srs = osr.SpatialReference()
740
+ srs.ImportFromWkt(self.wkt)
741
+ self.label.setText(srs.ExportToPrettyWkt())
742
+
743
+
744
+ class Metadata(QtGui.QDialog):
745
+ """
746
+ Edit Metadata
747
+
748
+ This class allows the editing of teh metadata for a raster dataset using a
749
+ GUI.
750
+
751
+ Attributes
752
+ ----------
753
+ name : oldtxt
754
+ old text
755
+ banddata : dictionary
756
+ band data
757
+ bandid : dictionary
758
+ dictionary of strings containing band names.
759
+ parent : parent
760
+ reference to the parent routine
761
+ indata : dictionary
762
+ dictionary of input datasets
763
+ outdata : dictionary
764
+ dictionary of output datasets
765
+ """
766
+ def __init__(self, parent=None):
767
+ QtGui.QDialog.__init__(self, parent)
768
+
769
+ self.indata = {}
770
+ self.outdata = {}
771
+ self.banddata = {}
772
+ self.dataid = {}
773
+ self.oldtxt = ''
774
+ self.parent = parent
775
+
776
+ self.groupbox = QtGui.QGroupBox()
777
+ self.combobox_bandid = QtGui.QComboBox()
778
+ self.pb_rename_id = QtGui.QPushButton()
779
+ self.lbl_rows = QtGui.QLabel()
780
+ self.lbl_cols = QtGui.QLabel()
781
+ self.inp_epsg_info = QtGui.QLabel()
782
+ self.txt_null = QtGui.QLineEdit()
783
+ self.dsb_tlx = QtGui.QLineEdit()
784
+ self.dsb_tly = QtGui.QLineEdit()
785
+ self.dsb_xdim = QtGui.QLineEdit()
786
+ self.dsb_ydim = QtGui.QLineEdit()
787
+ self.led_units = QtGui.QLineEdit()
788
+ self.lbl_min = QtGui.QLabel()
789
+ self.lbl_max = QtGui.QLabel()
790
+ self.lbl_mean = QtGui.QLabel()
791
+
792
+ self.proj = GroupProj('Input Projection')
793
+
794
+ self.setupui()
795
+
796
+ def setupui(self):
797
+ """ Setup UI """
798
+ gridlayout_main = QtGui.QGridLayout(self)
799
+ buttonbox = QtGui.QDialogButtonBox()
800
+
801
+ gridlayout = QtGui.QGridLayout(self.groupbox)
802
+ label_tlx = QtGui.QLabel()
803
+ label_tly = QtGui.QLabel()
804
+ label_xdim = QtGui.QLabel()
805
+ label_ydim = QtGui.QLabel()
806
+ label_null = QtGui.QLabel()
807
+ label_rows = QtGui.QLabel()
808
+ label_cols = QtGui.QLabel()
809
+ label_min = QtGui.QLabel()
810
+ label_max = QtGui.QLabel()
811
+ label_mean = QtGui.QLabel()
812
+ label_units = QtGui.QLabel()
813
+ label_bandid = QtGui.QLabel()
814
+
815
+ sizepolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred,
816
+ QtGui.QSizePolicy.Expanding)
817
+ self.groupbox.setSizePolicy(sizepolicy)
818
+ buttonbox.setOrientation(QtCore.Qt.Horizontal)
819
+ buttonbox.setCenterButtons(True)
820
+ buttonbox.setStandardButtons(buttonbox.Cancel | buttonbox.Ok)
821
+
822
+ self.setWindowTitle("Dataset Metadata")
823
+ self.pb_rename_id.setText("Rename Band Name")
824
+ self.groupbox.setTitle("Dataset")
825
+ label_bandid.setText('Band Name:')
826
+ label_tlx.setText("Top Left X Coordinate:")
827
+ label_tly.setText("Top Left Y Coordinate:")
828
+ label_xdim.setText("X Dimension:")
829
+ label_ydim.setText("Y Dimension:")
830
+ label_null.setText("Null/Nodata value:")
831
+ label_rows.setText("Rows:")
832
+ label_cols.setText("Columns:")
833
+ label_min.setText("Dataset Minimum:")
834
+ label_max.setText("Dataset Maximum:")
835
+ label_mean.setText("Dataset Mean:")
836
+ label_units.setText("Dataset Units:")
837
+
838
+ gridlayout_main.addWidget(label_bandid, 0, 0, 1, 1)
839
+ gridlayout_main.addWidget(self.combobox_bandid, 0, 1, 1, 3)
840
+ gridlayout_main.addWidget(self.pb_rename_id, 1, 1, 1, 3)
841
+ gridlayout_main.addWidget(self.groupbox, 2, 0, 1, 2)
842
+ gridlayout_main.addWidget(self.proj, 2, 2, 1, 2)
843
+ gridlayout_main.addWidget(buttonbox, 4, 0, 1, 4)
844
+
845
+ gridlayout.addWidget(label_tlx, 0, 0, 1, 1)
846
+ gridlayout.addWidget(self.dsb_tlx, 0, 1, 1, 1)
847
+ gridlayout.addWidget(label_tly, 1, 0, 1, 1)
848
+ gridlayout.addWidget(self.dsb_tly, 1, 1, 1, 1)
849
+ gridlayout.addWidget(label_xdim, 2, 0, 1, 1)
850
+ gridlayout.addWidget(self.dsb_xdim, 2, 1, 1, 1)
851
+ gridlayout.addWidget(label_ydim, 3, 0, 1, 1)
852
+ gridlayout.addWidget(self.dsb_ydim, 3, 1, 1, 1)
853
+ gridlayout.addWidget(label_null, 4, 0, 1, 1)
854
+ gridlayout.addWidget(self.txt_null, 4, 1, 1, 1)
855
+ gridlayout.addWidget(label_rows, 5, 0, 1, 1)
856
+ gridlayout.addWidget(self.lbl_rows, 5, 1, 1, 1)
857
+ gridlayout.addWidget(label_cols, 6, 0, 1, 1)
858
+ gridlayout.addWidget(self.lbl_cols, 6, 1, 1, 1)
859
+ gridlayout.addWidget(label_min, 7, 0, 1, 1)
860
+ gridlayout.addWidget(self.lbl_min, 7, 1, 1, 1)
861
+ gridlayout.addWidget(label_max, 8, 0, 1, 1)
862
+ gridlayout.addWidget(self.lbl_max, 8, 1, 1, 1)
863
+ gridlayout.addWidget(label_mean, 9, 0, 1, 1)
864
+ gridlayout.addWidget(self.lbl_mean, 9, 1, 1, 1)
865
+ gridlayout.addWidget(label_units, 10, 0, 1, 1)
866
+ gridlayout.addWidget(self.led_units, 10, 1, 1, 1)
867
+
868
+ buttonbox.accepted.connect(self.accept)
869
+ buttonbox.rejected.connect(self.reject)
870
+
871
+ self.combobox_bandid.currentIndexChanged.connect(self.update_vals)
872
+ self.pb_rename_id.clicked.connect(self.rename_id)
873
+
874
+ def acceptall(self):
875
+ """
876
+ This routine is called by settings() if accept is pressed. It contains
877
+ the main routine.
878
+ """
879
+ wkt = self.proj.wkt
880
+
881
+ self.update_vals()
882
+ for tmp in self.indata['Raster']:
883
+ for j in self.dataid.items():
884
+ if j[1] == tmp.dataid:
885
+ i = self.banddata[j[0]]
886
+ tmp.dataid = j[0]
887
+ tmp.tlx = i.tlx
888
+ tmp.tly = i.tly
889
+ tmp.xdim = i.xdim
890
+ tmp.ydim = i.ydim
891
+ tmp.rows = i.rows
892
+ tmp.cols = i.cols
893
+ tmp.nullvalue = i.nullvalue
894
+ tmp.wkt = wkt
895
+ tmp.units = i.units
896
+ if tmp.dataid[-1] == ')':
897
+ tmp.dataid = tmp.dataid[:tmp.dataid.rfind(' (')]
898
+ if i.units != '':
899
+ tmp.dataid += ' ('+i.units+')'
900
+ # tmp.data.data[tmp.data.data == i.nullvalue] = np.nan
901
+ tmp.data.mask = (tmp.data.data == i.nullvalue)
902
+
903
+ def rename_id(self):
904
+ """ Renames the band name """
905
+ ctxt = str(self.combobox_bandid.currentText())
906
+ (skey, isokay) = QtGui.QInputDialog.getText(
907
+ self.parent, 'Rename Band Name',
908
+ 'Please type in the new name for the band',
909
+ QtGui.QLineEdit.Normal, ctxt)
910
+
911
+ if isokay:
912
+ self.combobox_bandid.currentIndexChanged.disconnect()
913
+ indx = self.combobox_bandid.currentIndex()
914
+ txt = self.combobox_bandid.itemText(indx)
915
+ self.banddata[skey] = self.banddata.pop(txt)
916
+ self.dataid[skey] = self.dataid.pop(txt)
917
+ self.oldtxt = skey
918
+ self.combobox_bandid.setItemText(indx, skey)
919
+ self.combobox_bandid.currentIndexChanged.connect(self.update_vals)
920
+
921
+ def update_vals(self):
922
+ """ Updates the values on the interface """
923
+ odata = self.banddata[self.oldtxt]
924
+ odata.units = self.led_units.text()
925
+
926
+ try:
927
+ odata.nullvalue = float(self.txt_null.text())
928
+ odata.tlx = float(self.dsb_tlx.text())
929
+ odata.tly = float(self.dsb_tly.text())
930
+ odata.xdim = float(self.dsb_xdim.text())
931
+ odata.ydim = float(self.dsb_ydim.text())
932
+ except ValueError:
933
+ self.parent.showprocesslog('Value error - abandoning changes')
934
+
935
+ indx = self.combobox_bandid.currentIndex()
936
+ txt = self.combobox_bandid.itemText(indx)
937
+ self.oldtxt = txt
938
+ idata = self.banddata[txt]
939
+
940
+ self.lbl_cols.setText(str(idata.cols))
941
+ self.lbl_rows.setText(str(idata.rows))
942
+ self.txt_null.setText(str(idata.nullvalue))
943
+ self.dsb_tlx.setText(str(idata.tlx))
944
+ self.dsb_tly.setText(str(idata.tly))
945
+ self.dsb_xdim.setText(str(idata.xdim))
946
+ self.dsb_ydim.setText(str(idata.ydim))
947
+ self.lbl_min.setText(str(idata.min))
948
+ self.lbl_max.setText(str(idata.max))
949
+ self.lbl_mean.setText(str(idata.mean))
950
+ self.led_units.setText(str(idata.units))
951
+
952
+ def run(self):
953
+ """ Entrypoint to start this routine """
954
+
955
+ bandid = []
956
+ self.proj.set_current(self.indata['Raster'][0].wkt)
957
+
958
+ for i in self.indata['Raster']:
959
+ bandid.append(i.dataid)
960
+ self.banddata[i.dataid] = Data()
961
+ tmp = self.banddata[i.dataid]
962
+ self.dataid[i.dataid] = i.dataid
963
+ tmp.tlx = i.tlx
964
+ tmp.tly = i.tly
965
+ tmp.xdim = i.xdim
966
+ tmp.ydim = i.ydim
967
+ tmp.rows = i.rows
968
+ tmp.cols = i.cols
969
+ tmp.nullvalue = i.nullvalue
970
+ tmp.wkt = i.wkt
971
+ tmp.min = i.data.min()
972
+ tmp.max = i.data.max()
973
+ tmp.mean = i.data.mean()
974
+ try:
975
+ tmp.units = i.units
976
+ except AttributeError:
977
+ tmp.units = ''
978
+
979
+ self.combobox_bandid.currentIndexChanged.disconnect()
980
+ self.combobox_bandid.addItems(bandid)
981
+ indx = self.combobox_bandid.currentIndex()
982
+ self.oldtxt = self.combobox_bandid.itemText(indx)
983
+ self.combobox_bandid.currentIndexChanged.connect(self.update_vals)
984
+
985
+ idata = self.banddata[self.oldtxt]
986
+ self.lbl_cols.setText(str(idata.cols))
987
+ self.lbl_rows.setText(str(idata.rows))
988
+ self.txt_null.setText(str(idata.nullvalue))
989
+ self.dsb_tlx.setText(str(idata.tlx))
990
+ self.dsb_tly.setText(str(idata.tly))
991
+ self.dsb_xdim.setText(str(idata.xdim))
992
+ self.dsb_ydim.setText(str(idata.ydim))
993
+ self.lbl_min.setText(str(idata.min))
994
+ self.lbl_max.setText(str(idata.max))
995
+ self.lbl_mean.setText(str(idata.mean))
996
+ self.led_units.setText(str(idata.units))
997
+
998
+ self.update_vals()
999
+
1000
+ tmp = self.exec_()
1001
+ if tmp == 1:
1002
+ self.acceptall()
1003
+ tmp = True
1004
+
1005
+ return tmp
1006
+
1007
+
1008
+ class RTP(QtGui.QDialog):
1009
+ """
1010
+ Perform Reduction to the Pole on Magnetic data.
1011
+
1012
+ This class grids point data using a nearest neighbourhood technique.
1013
+
1014
+ Attributes
1015
+ ----------
1016
+ parent : parent
1017
+ reference to the parent routine
1018
+ indata : dictionary
1019
+ dictionary of input datasets
1020
+ outdata : dictionary
1021
+ dictionary of output datasets
1022
+ """
1023
+ def __init__(self, parent=None):
1024
+ QtGui.QDialog.__init__(self, parent)
1025
+
1026
+ self.indata = {}
1027
+ self.outdata = {}
1028
+ self.parent = parent
1029
+ self.pbar = self.parent.pbar
1030
+
1031
+ self.dataid = QtGui.QComboBox()
1032
+ self.dsb_inc = QtGui.QDoubleSpinBox()
1033
+ self.dsb_dec = QtGui.QDoubleSpinBox()
1034
+
1035
+ self.setupui()
1036
+
1037
+ def setupui(self):
1038
+ """ Setup UI """
1039
+ gridlayout_main = QtGui.QGridLayout(self)
1040
+ buttonbox = QtGui.QDialogButtonBox()
1041
+ helpdocs = menu_default.HelpButton('pygmi.raster.dataprep.rtp')
1042
+ label_band = QtGui.QLabel()
1043
+ label_inc = QtGui.QLabel()
1044
+ label_dec = QtGui.QLabel()
1045
+
1046
+ self.dsb_inc.setMaximum(90.0)
1047
+ self.dsb_inc.setMinimum(-90.0)
1048
+ self.dsb_dec.setMaximum(360.0)
1049
+ self.dsb_dec.setMinimum(-360.0)
1050
+ # self.dsb_dxy.setDecimals(5)
1051
+ buttonbox.setOrientation(QtCore.Qt.Horizontal)
1052
+ buttonbox.setCenterButtons(True)
1053
+ buttonbox.setStandardButtons(buttonbox.Cancel | buttonbox.Ok)
1054
+
1055
+ self.setWindowTitle("Reduction to the Pole")
1056
+ label_band.setText("Band to Reduce to the Pole:")
1057
+ label_inc.setText("Inclination of Magnetic Field:")
1058
+ label_dec.setText("Declination of Magnetic Field:")
1059
+
1060
+ gridlayout_main.addWidget(label_band, 0, 0, 1, 1)
1061
+ gridlayout_main.addWidget(self.dataid, 0, 1, 1, 1)
1062
+
1063
+ gridlayout_main.addWidget(label_inc, 1, 0, 1, 1)
1064
+ gridlayout_main.addWidget(self.dsb_inc, 1, 1, 1, 1)
1065
+ gridlayout_main.addWidget(label_dec, 2, 0, 1, 1)
1066
+ gridlayout_main.addWidget(self.dsb_dec, 2, 1, 1, 1)
1067
+ gridlayout_main.addWidget(helpdocs, 3, 0, 1, 1)
1068
+ gridlayout_main.addWidget(buttonbox, 3, 1, 1, 3)
1069
+
1070
+ buttonbox.accepted.connect(self.accept)
1071
+ buttonbox.rejected.connect(self.reject)
1072
+
1073
+ def settings(self):
1074
+ """ Settings """
1075
+ tmp = []
1076
+ if 'Raster' not in self.indata:
1077
+ return False
1078
+
1079
+ for i in self.indata['Raster']:
1080
+ tmp.append(i.dataid)
1081
+
1082
+ self.dataid.addItems(tmp)
1083
+
1084
+ self.dsb_inc.setValue(-62.5)
1085
+ self.dsb_dec.setValue(-16.75)
1086
+ tmp = self.exec_()
1087
+
1088
+ if tmp == 1:
1089
+ self.acceptall()
1090
+ tmp = True
1091
+
1092
+ return tmp
1093
+
1094
+ def acceptall(self):
1095
+ """ accept """
1096
+ I_deg = self.dsb_inc.value()
1097
+ D_deg = self.dsb_dec.value()
1098
+
1099
+ newdat = []
1100
+ for data in self.pbar.iter(self.indata['Raster']):
1101
+ if data.dataid != self.dataid.currentText():
1102
+ continue
1103
+ dat = rtp(data, I_deg, D_deg)
1104
+ newdat.append(dat)
1105
+
1106
+ self.outdata['Raster'] = newdat
1107
+
1108
+
1109
+ def rtp(data, I_deg, D_deg):
1110
+ """ Reduction to the Pole """
1111
+
1112
+ datamedian = np.ma.median(data.data)
1113
+ ndat = data.data - datamedian
1114
+ ndat[ndat.mask] = 0
1115
+
1116
+ fftmod = np.fft.fft2(ndat)
1117
+
1118
+ ny, nx = fftmod.shape
1119
+ nyqx = 1/(2*data.xdim)
1120
+ nyqy = 1/(2*data.ydim)
1121
+
1122
+ kx = np.linspace(-nyqx, nyqx, nx)
1123
+ ky = np.linspace(-nyqy, nyqy, ny)
1124
+
1125
+ KX, KY = np.meshgrid(kx, ky)
1126
+
1127
+ I = np.deg2rad(I_deg)
1128
+ D = np.deg2rad(D_deg)
1129
+ alpha = np.arctan2(KX, KY)
1130
+ filt = 1/(np.sin(I)+1j*np.cos(I)*np.cos(D-alpha))**2
1131
+
1132
+ zrtp = np.fft.ifft2(fftmod*filt)
1133
+ zrtp = np.abs(zrtp) + datamedian
1134
+
1135
+ # Create dataset
1136
+ dat = Data()
1137
+ dat.data = np.ma.masked_invalid(np.abs(zrtp))
1138
+ dat.data.mask = data.data.mask
1139
+ dat.rows, dat.cols = zrtp.shape
1140
+ dat.nullvalue = data.data.fill_value
1141
+ dat.dataid = data.dataid
1142
+ dat.tlx = data.tlx
1143
+ dat.tly = data.tly
1144
+ dat.xdim = data.xdim
1145
+ dat.ydim = data.ydim
1146
+
1147
+ return dat
1148
+
1149
+
1150
+ def check_dataid(out):
1151
+ """ Checks dataid for duplicates and renames where necessary """
1152
+ tmplist = []
1153
+ for i in out:
1154
+ tmplist.append(i.dataid)
1155
+
1156
+ tmpcnt = Counter(tmplist)
1157
+ for elt, count in tmpcnt.items():
1158
+ j = 1
1159
+ for i in out:
1160
+ if elt == i.dataid and count > 1:
1161
+ i.dataid += '('+str(j)+')'
1162
+ j += 1
1163
+
1164
+ return out
1165
+
1166
+
1167
+ def cluster_to_raster(indata):
1168
+ """ Converts cluster datasets to raster datasets
1169
+
1170
+ Some routines will not understand the datasets produced by cluster
1171
+ analysis routines, since they are designated 'Cluster' and not 'Raster'.
1172
+ This provides a work-around for that.
1173
+
1174
+ Parameters
1175
+ ----------
1176
+ indata : Data
1177
+ PyGMI raster dataset
1178
+
1179
+ Returns
1180
+ -------
1181
+ Data
1182
+ PyGMI raster dataset
1183
+
1184
+ """
1185
+ if 'Cluster' not in indata:
1186
+ return indata
1187
+ if 'Raster' not in indata:
1188
+ indata['Raster'] = []
1189
+
1190
+ for i in indata['Cluster']:
1191
+ indata['Raster'].append(i)
1192
+ indata['Raster'][-1].data += 1
1193
+
1194
+ return indata
1195
+
1196
+
1197
+ def cut_raster(data, ifile):
1198
+ """Cuts a raster dataset
1199
+
1200
+ Cut a raster dataset using a shapefile
1201
+
1202
+ Parameters
1203
+ ----------
1204
+ data : Data
1205
+ PyGMI Dataset
1206
+ ifile : str
1207
+ shapefile used to cut data
1208
+
1209
+ Returns
1210
+ -------
1211
+ Data
1212
+ PyGMI Dataset
1213
+ """
1214
+ shapef = ogr.Open(ifile)
1215
+ if shapef is None:
1216
+ return None
1217
+ lyr = shapef.GetLayer()
1218
+ poly = lyr.GetNextFeature()
1219
+ if lyr.GetGeomType() is not ogr.wkbPolygon or poly is None:
1220
+ # self.parent.showprocesslog('You need polygons in that shape file')
1221
+ return
1222
+
1223
+ for idata in data:
1224
+ # Convert the layer extent to image pixel coordinates
1225
+ minX, maxX, minY, maxY = lyr.GetExtent()
1226
+ ulX = max(0, int((minX - idata.tlx) / idata.xdim))
1227
+ ulY = max(0, int((idata.tly - maxY) / idata.ydim))
1228
+ lrX = int((maxX - idata.tlx) / idata.xdim)
1229
+ lrY = int((idata.tly - minY) / idata.ydim)
1230
+
1231
+ # Map points to pixels for drawing the
1232
+ # boundary on a mas image
1233
+ points = []
1234
+ pixels = []
1235
+ geom = poly.GetGeometryRef()
1236
+
1237
+ ifin = 0
1238
+ imax = 0
1239
+ if geom.GetGeometryName() == 'MULTIPOLYGON':
1240
+ for i in range(geom.GetGeometryCount()):
1241
+ geom.GetGeometryRef(i)
1242
+ itmp = geom.GetGeometryRef(i)
1243
+ itmp = itmp.GetGeometryRef(0).GetPointCount()
1244
+ if itmp > imax:
1245
+ imax = itmp
1246
+ ifin = i
1247
+ geom = geom.GetGeometryRef(ifin)
1248
+
1249
+ pts = geom.GetGeometryRef(0)
1250
+ for p in range(pts.GetPointCount()):
1251
+ points.append((pts.GetX(p), pts.GetY(p)))
1252
+ for p in points:
1253
+ tmpx = int((p[0] - idata.tlx) / idata.xdim)
1254
+ tmpy = int((idata.tly - p[1]) / idata.ydim)
1255
+ pixels.append((tmpx, tmpy))
1256
+ rasterPoly = Image.new("L", (idata.cols, idata.rows), 1)
1257
+ rasterize = ImageDraw.Draw(rasterPoly)
1258
+ rasterize.polygon(pixels, 0)
1259
+ mask = np.array(rasterPoly)
1260
+
1261
+ idata.data.mask = mask
1262
+ idata.data = idata.data[ulY:lrY, ulX:lrX]
1263
+ idata.cols = idata.data.shape[1]
1264
+ idata.rows = idata.data.shape[0]
1265
+ idata.tlx = ulX*idata.xdim + idata.tlx # minX
1266
+ idata.tly = idata.tly - ulY*idata.ydim # maxY
1267
+ data = trim_raster(data)
1268
+ return data
1269
+
1270
+
1271
+ def dat_extent(dat):
1272
+ """ Gets the extend of the dat variable """
1273
+ left = dat.tlx
1274
+ top = dat.tly
1275
+ right = left + dat.cols*dat.xdim
1276
+ bottom = top - dat.rows*dat.ydim
1277
+ return (left, right, bottom, top)
1278
+
1279
+
1280
+ def data_to_gdal_mem(data, gtr, wkt, cols, rows, nodata=False):
1281
+ """
1282
+ Data to GDAL mem format
1283
+
1284
+ Parameters
1285
+ ----------
1286
+ data : PyGMI Data
1287
+ PyGMI Dataset
1288
+ gtr : tuple
1289
+ Geotransform
1290
+ wkt : str
1291
+ Projection in wkt (well known text) format
1292
+ cols : int
1293
+ columns
1294
+ rows : int
1295
+ rows
1296
+ nodata : bool, optional
1297
+ no data
1298
+
1299
+ Returns
1300
+ -------
1301
+ src - GDAL mem format
1302
+ """
1303
+ data.data = np.ma.array(data.data)
1304
+ dtype = data.data.dtype
1305
+ # Get rid of array() which can break driver.create later
1306
+ cols = int(cols)
1307
+ rows = int(rows)
1308
+
1309
+ if dtype == np.uint8:
1310
+ fmt = gdal.GDT_Byte
1311
+ elif dtype == np.int32:
1312
+ fmt = gdal.GDT_Int32
1313
+ elif dtype == np.float64:
1314
+ fmt = gdal.GDT_Float64
1315
+ else:
1316
+ fmt = gdal.GDT_Float32
1317
+
1318
+ driver = gdal.GetDriverByName('MEM')
1319
+ src = driver.Create('', cols, rows, 1, fmt)
1320
+
1321
+ src.SetGeoTransform(gtr)
1322
+ src.SetProjection(wkt)
1323
+
1324
+ if nodata is False:
1325
+ if data.nullvalue is not None:
1326
+ src.GetRasterBand(1).SetNoDataValue(data.nullvalue)
1327
+ src.GetRasterBand(1).WriteArray(data.data)
1328
+ else:
1329
+ tmp = np.ma.masked_all((rows, cols))
1330
+ src.GetRasterBand(1).SetNoDataValue(0) # Set to this because of Reproj
1331
+ src.GetRasterBand(1).WriteArray(tmp)
1332
+
1333
+ return src
1334
+
1335
+
1336
+ def epsgtowkt(epsg):
1337
+ """ Convenience routine to get a wkt from an epsg code """
1338
+ orig = osr.SpatialReference()
1339
+ err = orig.ImportFromEPSG(int(epsg))
1340
+ if err != 0:
1341
+ return ''
1342
+ out = orig.ExportToWkt()
1343
+ return out
1344
+
1345
+
1346
+ def gdal_to_dat(dest, bandid='Data'):
1347
+ """
1348
+ GDAL to Data format
1349
+
1350
+ Parameters
1351
+ ----------
1352
+ dest - GDAL format
1353
+ GDAL format
1354
+ bandid - str
1355
+ band identity
1356
+ """
1357
+ dat = Data()
1358
+ gtr = dest.GetGeoTransform()
1359
+
1360
+ rtmp = dest.GetRasterBand(1)
1361
+ dat.data = rtmp.ReadAsArray()
1362
+ nval = rtmp.GetNoDataValue()
1363
+ dat.data = np.ma.masked_equal(dat.data, nval)
1364
+
1365
+ # dtype = dat.data.dtype
1366
+ # nval = np.nan
1367
+ # if dtype == np.float32 or dtype == np.float64:
1368
+ # dat.data[dat.data == 0.] = np.nan
1369
+ # # dat.data[dat.data == rtmp.GetNoDataValue()] = np.nan
1370
+ # dat.data = np.ma.masked_invalid(nval)
1371
+ #
1372
+ # if dtype == np.uint8:
1373
+ # dat.data = np.ma.masked_equal(dat.data, 0)
1374
+ # nval = 0
1375
+
1376
+ # dat.data[dat.data.mask] = rtmp.GetNoDataValue()
1377
+
1378
+ dat.nrofbands = dest.RasterCount
1379
+ dat.tlx = gtr[0]
1380
+ dat.tly = gtr[3]
1381
+ dat.dataid = bandid
1382
+ dat.nullvalue = nval
1383
+ dat.rows = dest.RasterYSize
1384
+ dat.cols = dest.RasterXSize
1385
+ dat.xdim = abs(gtr[1])
1386
+ dat.ydim = abs(gtr[5])
1387
+ dat.wkt = dest.GetProjection()
1388
+ dat.gtr = gtr
1389
+
1390
+ return dat
1391
+
1392
+
1393
+ def getepsgcodes():
1394
+ """
1395
+ Convenience function used to get a list of EPSG codes
1396
+ """
1397
+ dfile = open(os.environ['GDAL_DATA']+'\\gcs.csv')
1398
+ dlines = dfile.readlines()
1399
+ dfile.close()
1400
+
1401
+ dlines = dlines[1:]
1402
+ dcodes = {}
1403
+ for i in dlines:
1404
+ tmp = i.split(',')
1405
+ if tmp[1][0] == '"':
1406
+ tmp[1] = tmp[1][1:]
1407
+ if tmp[1][-1] == '"':
1408
+ tmp[1] = tmp[1][:-1]
1409
+ wkttmp = epsgtowkt(tmp[0])
1410
+ if wkttmp != '':
1411
+ dcodes[tmp[1]] = wkttmp
1412
+
1413
+ pfile = open(os.environ['GDAL_DATA']+'\\pcs.csv')
1414
+ plines = pfile.readlines()
1415
+ pfile.close()
1416
+
1417
+ pcodes = {}
1418
+ for i in dcodes.keys():
1419
+ pcodes[i+r' / Geodetic Geographic'] = dcodes[i]
1420
+
1421
+ plines = plines[1:]
1422
+ for i in plines:
1423
+ tmp = i.split(',')
1424
+ if tmp[1][0] == '"':
1425
+ tmp[1] = tmp[1][1:]
1426
+ if tmp[1][-1] == '"':
1427
+ tmp[1] = tmp[1][:-1]
1428
+ wkttmp = epsgtowkt(tmp[0])
1429
+ if wkttmp != '':
1430
+ pcodes[tmp[1]] = wkttmp
1431
+
1432
+ clat = 0.
1433
+ scale = 1.
1434
+ f_e = 0.
1435
+ f_n = 0.
1436
+ orig = osr.SpatialReference()
1437
+
1438
+ for datum in ['Cape', 'Hartebeesthoek94']:
1439
+ orig.ImportFromWkt(dcodes[datum])
1440
+ for clong in range(15, 35, 2):
1441
+ orig.SetTM(clat, clong, scale, f_e, f_n)
1442
+ orig.SetProjCS(datum+r' / TM'+str(clong))
1443
+ pcodes[datum+r' / TM'+str(clong)] = orig.ExportToWkt()
1444
+
1445
+ return pcodes
1446
+
1447
+
1448
+ def merge(dat):
1449
+ """ Merges datasets found in a single PyGMI data object.
1450
+
1451
+ The aim is to ensure that all datasets have the same number of rows and
1452
+ columns.
1453
+
1454
+ Parameters
1455
+ ----------
1456
+ dat : Data
1457
+ data object which stores datasets
1458
+
1459
+ Returns
1460
+ -------
1461
+ Data
1462
+ data object which stores datasets
1463
+ """
1464
+ needsmerge = False
1465
+ for i in dat:
1466
+ if i.rows != dat[0].rows or i.cols != dat[0].cols:
1467
+ needsmerge = True
1468
+
1469
+ if needsmerge is False:
1470
+ dat = check_dataid(dat)
1471
+ return dat
1472
+
1473
+ mrg = DataMerge()
1474
+ mrg.indata['Raster'] = dat
1475
+ data = dat[0]
1476
+ dxy0 = min(data.xdim, data.ydim)
1477
+ for data in dat:
1478
+ dxy = min(dxy0, data.xdim, data.ydim)
1479
+
1480
+ mrg.dsb_dxy.setValue(dxy)
1481
+ mrg.acceptall()
1482
+ out = mrg.outdata['Raster']
1483
+
1484
+ out = check_dataid(out)
1485
+
1486
+ return out
1487
+
1488
+
1489
+ #def taper(data):
1490
+ # nr, nc = data.shape
1491
+ # nmax = np.max([nr, nc])
1492
+ # npts = int(2**cooper.__nextpow2(nmax))
1493
+ # npts *= 2
1494
+ #
1495
+ # cdiff = int(np.floor((npts-nc)/2))
1496
+ # rdiff = int(np.floor((npts-nr)/2))
1497
+ # data1 = cooper.__taper2d(data, npts, nc, nr, cdiff, rdiff)
1498
+ ## data1 = np.pad(data-np.median(data), ((rdiff, cdiff), (rdiff,cdiff)), 'edge')
1499
+
1500
+ # return data1
1501
+
1502
+
1503
+ def trim_raster(olddata):
1504
+ """ Function to trim nulls from a raster dataset.
1505
+
1506
+ This function trims entire rows or columns of data which have only nulls,
1507
+ and are on the edges of the dataset.
1508
+
1509
+ Parameters
1510
+ ----------
1511
+ olddata : Data
1512
+ PyGMI dataset
1513
+
1514
+ Returns
1515
+ -------
1516
+ Data
1517
+ PyGMI dataset
1518
+ """
1519
+
1520
+ for data in olddata:
1521
+ mask = data.data.mask
1522
+ data.data[mask] = data.nullvalue
1523
+
1524
+ rowstart = 0
1525
+ for i in range(mask.shape[0]):
1526
+ if bool(mask[i].min()) is False:
1527
+ break
1528
+ rowstart += 1
1529
+
1530
+ rowend = mask.shape[0]
1531
+ for i in range(mask.shape[0]-1, -1, -1):
1532
+ if bool(mask[i].min()) is False:
1533
+ break
1534
+ rowend -= 1
1535
+
1536
+ colstart = 0
1537
+ for i in range(mask.shape[1]):
1538
+ if bool(mask[:, i].min()) is False:
1539
+ break
1540
+ colstart += 1
1541
+
1542
+ colend = mask.shape[1]
1543
+ for i in range(mask.shape[1]-1, -1, -1):
1544
+ if bool(mask[:, i].min()) is False:
1545
+ break
1546
+ colend -= 1
1547
+
1548
+ data.data = data.data[rowstart:rowend, colstart:colend]
1549
+ data.data.mask = (data.data.data == data.nullvalue)
1550
+ # data.data = np.ma.masked_invalid(data.data[rowstart:rowend,
1551
+ # colstart:colend])
1552
+ data.rows, data.cols = data.data.shape
1553
+ data.tlx = data.tlx + colstart*data.xdim
1554
+ data.tly = data.tly - rowstart*data.ydim
1555
+
1556
+ return olddata
1557
+
1558
+
1559
+ def quickgrid(x, y, z, dxy, showtext=None, numits=4):
1560
+ """
1561
+ Do a quick grid
1562
+
1563
+ Parameters
1564
+ ----------
1565
+ x : numpy array
1566
+ array of x coordinates
1567
+ y : numpy array
1568
+ array of y coordinates
1569
+ z : numpy array
1570
+ array of z values - this is the column being gridded
1571
+ dxy : float
1572
+ cell size for the grid, in both the x and y direction.
1573
+ showtext : module, optional
1574
+ showtext provided an alternative to print
1575
+ numits : int
1576
+ number of iterations. By default its 4. If this is negative, a maximum
1577
+ numits will be calculated and used.
1578
+
1579
+ Returns
1580
+ -------
1581
+ newz : numpy array
1582
+ M x N array of z values
1583
+ """
1584
+
1585
+ if showtext is None:
1586
+ showtext = print
1587
+
1588
+ showtext('Creating Grid')
1589
+
1590
+ xmin = x.min()
1591
+ xmax = x.max()
1592
+ ymin = y.min()
1593
+ ymax = y.max()
1594
+ newmask = np.array([1])
1595
+ j = -1
1596
+ rows = int((ymax-ymin)/dxy)+1
1597
+ cols = int((xmax-xmin)/dxy)+1
1598
+
1599
+ if numits < 1:
1600
+ numits = int(max(np.log2(cols), np.log2(rows)))
1601
+
1602
+ while newmask.max() > 0 and j < (numits-1):
1603
+ j += 1
1604
+ jj = 2**j
1605
+ dxy2 = dxy*jj
1606
+ rows = int((ymax-ymin)/dxy2)+1
1607
+ cols = int((xmax-xmin)/dxy2)+1
1608
+
1609
+ newz = np.zeros([rows, cols])
1610
+ zdiv = np.zeros([rows, cols])
1611
+
1612
+ xindex = ((x-xmin)/dxy2).astype(int)
1613
+ yindex = ((y-ymin)/dxy2).astype(int)
1614
+
1615
+ for i in range(z.size):
1616
+ newz[yindex[i], xindex[i]] += z[i]
1617
+ zdiv[yindex[i], xindex[i]] += 1
1618
+
1619
+ filt = zdiv > 0
1620
+ newz[filt] = newz[filt]/zdiv[filt]
1621
+
1622
+ if j == 0:
1623
+ newmask = np.ones([rows, cols])
1624
+ for i in range(z.size):
1625
+ newmask[yindex[i], xindex[i]] = 0
1626
+ zfin = newz
1627
+ else:
1628
+ xx, yy = np.nonzero(newmask)
1629
+ xx2 = xx//jj
1630
+ yy2 = yy//jj
1631
+ zfin[xx, yy] = newz[xx2, yy2]
1632
+ newmask[xx, yy] = np.logical_not(zdiv[xx2, yy2])
1633
+
1634
+ showtext('Iteration done: '+str(j+1)+' of '+str(numits))
1635
+
1636
+ showtext('Finished!')
1637
+
1638
+ newz = np.ma.array(zfin)
1639
+ newz.mask = newmask
1640
+ return newz