source: issm/oecreview/Archive/24684-25833/ISSM-25064-25065.diff

Last change on this file was 25834, checked in by Mathieu Morlighem, 4 years ago

CHG: added 24684-25833

File size: 78.5 KB
RevLine 
[25834]1Index: ../trunk-jpl/src/m/shp/shpwrite.py
2===================================================================
3--- ../trunk-jpl/src/m/shp/shpwrite.py (nonexistent)
4+++ ../trunk-jpl/src/m/shp/shpwrite.py (revision 25065)
5@@ -0,0 +1,60 @@
6+try:
7+ import shapefile
8+except ImportError:
9+ print("could not import shapefile, PyShp has not been installed, no shapefile reading capabilities enabled")
10+
11+def shpwrite(shp, filename): #{{{
12+ '''
13+ SHPREAD - write a shape file from a contour structure
14+
15+ The current implementation of shpwrite depends on PyShp.
16+
17+ Usage:
18+ shpwrite(shp, filename)
19+
20+ Example:
21+ shpwrite(shp, 'domainoutline.shp')
22+
23+ See also SHPREAD
24+
25+ Sources:
26+ - https://github.com/GeospatialPython/pyshp
27+
28+ TODO:
29+ - Should we check if there is only one element (see how MATLAB's shaperead
30+ and shapewrite handle single shapes versus multiple shapes)?
31+ '''
32+
33+ # Sanity checks
34+ for shape in shp:
35+ print(shape)
36+
37+ if shp[0].Geometry == 'Point':
38+ shapeType = 1
39+ elif shp[0].Geometry == 'Line':
40+ shapeType = 3
41+ elif shp[0].Geometry == 'Polygon':
42+ shapeType = 5
43+ else:
44+ raise Exception('shpwrite error: geometry \'{}\' is not currently supported'.format(shp[0].Geometry))
45+
46+ sf = shapefile.Writer(filename, shapeType=shapeType)
47+
48+ for i in range(len(shp)):
49+ sf.field('name', 'C') # TODO: Allow shape ids/names to be passed through
50+ if shapeType == 1: # POINT
51+ sf.point(shp[i].x, shp[i].y)
52+ elif shapeType == 3: # POLYLINE
53+ points = []
54+ for j in range(len(shp[i].x)):
55+ points.append([shp[i].x[j], shp[i].y[j]])
56+ sf.line(line)
57+ elif shapeType == 5: # POLYGON
58+ points = []
59+ for j in range(len(shp[i].x)):
60+ points.append([shp[i].x[j], shp[i].y[j]])
61+ sf.poly(points)
62+ sf.null()
63+ sf.record(str(i))
64+ sf.close()
65+#}}}
66Index: ../trunk-jpl/src/m/shp/shpwrite.m
67===================================================================
68--- ../trunk-jpl/src/m/shp/shpwrite.m (revision 25064)
69+++ ../trunk-jpl/src/m/shp/shpwrite.m (revision 25065)
70@@ -9,10 +9,6 @@
71 %
72 % See also SHPREAD
73
74-
75-%initialize number of profile
76-count=0;
77-
78 contours=struct([]);
79 for i=1:length(shp),
80 if strcmpi(shp(i).Geometry,'Point'),
81@@ -21,11 +17,13 @@
82 contours(i).Geometry='Polygon';
83 elseif strcmpi(shp(i).Geometry,'Line'),
84 contours(i).Geometry='Line';
85+ else,
86+ error(['shpwrite error: geometry ' shp(i).Geometry ' not currently supported']);
87 end
88 contours(i).id=i;
89 contours(i).X=shp(i).x;
90 contours(i).Y=shp(i).y;
91 end
92-
93+
94 shapewrite(contours,filename);
95 end
96Index: ../trunk-jpl/src/m/mesh/meshintersect3d.py
97===================================================================
98--- ../trunk-jpl/src/m/mesh/meshintersect3d.py (revision 25064)
99+++ ../trunk-jpl/src/m/mesh/meshintersect3d.py (revision 25065)
100@@ -6,9 +6,11 @@
101
102 from pairoptions import pairoptions
103
104+
105 def meshintersect3d(x, y, z, xs, ys, zs, *args): #{{{
106 '''
107- MESHINTERSECT - returns indices (into x, y, and z) of common values between (x, y, z) and (xs, ys, zs) (i.e. x(index) = xs; y(index) = ys).
108+ MESHINTERSECT - returns indices (into x, y, and z) of common values between
109+ (x, y, z) and (xs, ys, zs) (i.e. x(index) = xs; y(index) = ys).
110 '''
111
112 # Process options
113Index: ../trunk-jpl/src/m/coordsystems/laea.py
114===================================================================
115--- ../trunk-jpl/src/m/coordsystems/laea.py (nonexistent)
116+++ ../trunk-jpl/src/m/coordsystems/laea.py (revision 25065)
117@@ -0,0 +1,15 @@
118+def laea(lat, long): #{{{
119+ '''
120+ LAEA - Lambert Azimuthal Equal Area projection at lat, long projection
121+ center.
122+
123+ Usage:
124+ string = laea(45, -90)
125+
126+ Example:
127+ string = laea(45, -90)
128+ return string = '+proj=laea +lat_0=45 +lon_0=-90 +x_0=0 +y_0=0 +ellps=WGS84 +units=m +no_defs'
129+ '''
130+
131+ return '+proj=laea +lat_0={} +lon_0={} +x_0=0 +y_0=0 +ellps=WGS84 +units=m +no_defs'.format(lat, long)
132+#}}}
133Index: ../trunk-jpl/src/m/coordsystems/gdaltransform.m
134===================================================================
135--- ../trunk-jpl/src/m/coordsystems/gdaltransform.m (revision 25064)
136+++ ../trunk-jpl/src/m/coordsystems/gdaltransform.m (revision 25065)
137@@ -19,9 +19,9 @@
138 % Bamber's Greeland projection
139 % +proj=stere +lat_0=90 +lat_ts=71 +lon_0=-39 +k=1 +x_0=0 +y_0=0 +a=6378273 +b=6356889.448564109 +units=m +no_defs
140 %
141-% To get proj.4 string from EPSG, use gdalsrsinfo. Example:
142+% To get proj.4 string from EPSG, use gdalsrsinfo. Example:
143 %
144-% gdalsrsinfo epsg:4326 | grep "PROJ.4" | sed "s/PROJ.4 : //"
145+% gdalsrsinfo epsg:4326 | grep "PROJ.4" | sed "s/PROJ.4 : //"
146
147 %give ourselves unique file names
148 filename_in = tempname();
149Index: ../trunk-jpl/src/m/geometry/AboveGround.py
150===================================================================
151--- ../trunk-jpl/src/m/geometry/AboveGround.py (nonexistent)
152+++ ../trunk-jpl/src/m/geometry/AboveGround.py (revision 25065)
153@@ -0,0 +1,8 @@
154+import numpy as np
155+
156+def function AboveGround(lat, long, r, height): #{{{
157+ r = r + height
158+ x = r * np.cos(np.deg2rad(lat)) * np.cos(np.deg2rad(long))
159+ y = r * np.cos(np.deg2rad(lat)) * np.sin(np.deg2rad(long))
160+ z = r * np.sin(np.deg2rad(lat))
161+#}}}
162Index: ../trunk-jpl/src/m/classes/plotoptions.py
163===================================================================
164--- ../trunk-jpl/src/m/classes/plotoptions.py (revision 25064)
165+++ ../trunk-jpl/src/m/classes/plotoptions.py (revision 25065)
166@@ -1,4 +1,5 @@
167 from collections import OrderedDict
168+
169 import pairoptions
170
171
172@@ -6,8 +7,8 @@
173 '''
174 PLOTOPTIONS class definition
175
176- Usage:
177- plotoptions = plotoptions(*arg)
178+ Usage:
179+ plotoptions = plotoptions(*arg)
180 '''
181
182 def __init__(self, *arg): # {{{
183@@ -35,7 +36,7 @@
184 def buildlist(self, *arg): #{{{
185 #check length of input
186 if len(arg) % 2:
187- raise TypeError('Invalid parameter / value pair arguments')
188+ raise TypeError('Invalid parameter/value pair arguments')
189
190 #go through args and build list (like pairoptions)
191 rawoptions = pairoptions.pairoptions(*arg)
192@@ -102,7 +103,7 @@
193 if len(nums) != 2:
194 continue
195 if False in [x.isdigit() for x in nums]:
196- raise ValueError('error: in option i - j both i and j must be integers')
197+ raise ValueError('error: in option i-j both i and j must be integers')
198 for j in range(int(nums[0]) - 1, int(nums[1])):
199 self.list[j].addfield(field, rawlist[i][1])
200
201Index: ../trunk-jpl/src/m/classes/basin.m
202===================================================================
203--- ../trunk-jpl/src/m/classes/basin.m (revision 25064)
204+++ ../trunk-jpl/src/m/classes/basin.m (revision 25065)
205@@ -18,42 +18,39 @@
206 end
207 methods
208 function self = basin(varargin) % {{{
209- switch nargin
210- case 0
211- self=setdefaultparameters(self);
212- otherwise
213+ self=setdefaultparameters(self);
214
215- self=setdefaultparameters(self);
216- options=pairoptions(varargin{:});
217-
218- %recover field values:
219- self.boundaries=getfieldvalue(options,'boundaries',{});
220- self.name=getfieldvalue(options,'name','');
221- self.continent=getfieldvalue(options,'continent','');
222+ if nargin>0,
223+ options=pairoptions(varargin{:});
224+
225+ %recover field values:
226+ self.boundaries=getfieldvalue(options,'boundaries',{});
227+ self.name=getfieldvalue(options,'name','');
228+ self.continent=getfieldvalue(options,'continent','');
229
230- %figure out projection string:
231+ %figure out projection string:
232+ if exist(options,'epsg'),
233+ if exist(options,'proj'),
234+ error(['Error in constructor for basin ' self.name '. Cannot supply epsg and proj at the same time!']);
235+ end
236+ epsg=getfieldvalue(options,'epsg');
237+ proj=epsg2proj(epsg); %convert to PROJ.4 format
238+ elseif exist(options,'proj'),
239 if exist(options,'epsg'),
240- if exist(options,'proj'),
241- error(['Error in constructor for boundary ' self.shppath '.Cannot supply epsg and proj at the same time!']);
242- end
243- epsg=getfieldvalue(options,'epsg');
244- %convert into proj4:
245- proj=epsg2proj(epsg);
246- elseif exist(options,'proj'),
247- if exist(options,'epsg'),
248- error(['Error in constructor for boundary ' self.shppath '.Cannot supply epsg and proj at the same time!']);
249- end
250- proj=getfieldvalue(options,'proj');
251- else
252- proj= '+proj=longlat +datum=WGS84 +no_defs';
253+ error(['Error in constructor for basin ' self.name '. Cannot supply proj and epsg at the same time!']);
254 end
255- self.proj=proj;
256+ proj=getfieldvalue(options,'proj');
257+ else
258+ proj='+proj=longlat +datum=WGS84 +no_defs';
259+ end
260+
261+ self.proj=proj;
262 end
263 end % }}}
264 function self = setdefaultparameters(self) % {{{
265 self.name='';
266 self.continent='';
267- self.proj='+proj=longlat +datum=WGS84 +no_defs '; %EPSG 4326
268+ self.proj='+proj=longlat +datum=WGS84 +no_defs'; %EPSG 4326
269 self.boundaries={};
270
271 end % }}}
272@@ -92,11 +89,11 @@
273 options=pairoptions(varargin{:});
274 extension=getfieldvalue(options,'extension',1);
275
276- [path,nme,ext]=fileparts(self.name);
277+ [path,name,ext]=fileparts(self.name);
278 if extension,
279- output=[nme ext];
280+ output=[name ext];
281 else
282- output=nme;
283+ output=name;
284 end
285 end % }}}
286 function plot(self,varargin) % {{{
287@@ -173,7 +170,7 @@
288 inshapefile=getfieldvalue(options,'shapefile');
289 outputshapefile=getfieldvalue(options,'outputshapefile','');
290
291- if (exist(options,'epsgshapefile') & exist(options,'projshapefile')),
292+ if (exist(options,'epsgshapefile') & exist(options,'projshapefile')),
293 error('Basin shapefilecrop error message: cannot specify epsgshapefile and projshapefile at the same time');
294 end
295 if exist(options,'epsgshapefile'),
296@@ -185,7 +182,7 @@
297 end
298
299
300- %create list of contours that have critical length > threshold: (in lat,long)
301+ %create list of contours that have critical length > threshold (in lat,long):
302 contours=shpread(inshapefile);
303 llist=[];
304 for i=1:length(contours),
305@@ -211,8 +208,8 @@
306 flags=zeros(length(contours),1);
307 for i=1:length(contours),
308 h=contours(i);
309- in=inpolygon(h.x,h.y,ba.x,ba.y);
310- if ~isempty(find(in==0)),
311+ isin=inpolygon(h.x,h.y,ba.x,ba.y);
312+ if ~isempty(find(isin==0)),
313 flags(i)=1;
314 end
315 end
316Index: ../trunk-jpl/src/m/qmu/helpers.py
317===================================================================
318--- ../trunk-jpl/src/m/qmu/helpers.py (revision 25064)
319+++ ../trunk-jpl/src/m/qmu/helpers.py (revision 25065)
320@@ -1,8 +1,9 @@
321-import numpy as np
322 from collections import OrderedDict
323 from copy import deepcopy
324
325+import numpy as np
326
327+
328 class struct(object):
329 '''An empty struct that can be assigned arbitrary attributes'''
330 pass
331@@ -10,40 +11,41 @@
332
333 class Lstruct(list):
334 '''
335-An empty struct that can be assigned arbitrary attributes
336- but can also be accesed as a list. Eg. x.y = 'hello', x[:] = ['w', 'o', 'r', 'l', 'd']
337+ An empty struct that can be assigned arbitrary attributes but can also be
338+ accesed as a list. Eg. x.y = 'hello', x[:] = ['w', 'o', 'r', 'l', 'd']
339
340-Note that 'x' returns the array and x.__dict__ will only return
341- attributes other than the array
342+ Note that 'x' returns the array and x.__dict__ will only return attributes
343+ other than the array
344
345-List-based and struct-based behaviors work normally, however they are referenced
346- as if the other does not exist; len(x) corresponds only to
347- the list component of x, len(x.a) corresponds to x.a, x.__dict__
348- corresponds only to the non-x-list attributes
349+ List-based and struct-based behaviors work normally, however they are
350+ referenced as if the other does not exist; len(x) corresponds only to the
351+ list component of x, len(x.a) corresponds to x.a, x.__dict__ corresponds
352+ only to the non-x-list attributes
353
354-Example uses:
355+ Examples:
356
357-x = Lstruct(1, 2, 3, 4) -> [1, 2, 3, 4]
358- x.a = 'hello'
359- len(x) -> 4
360- x.append(5)
361- len(x) -> 5
362- x[2] -> 3
363- x.a -> 'hello'
364- print x -> [1, 2, 3, 4, 5]
365- x.__dict__ -> {'a':'hello'}
366- x.b = [6, 7, 8, 9]
367- x.b[-1] -> 9
368- len(x.b) -> 4
369+ x = Lstruct(1, 2, 3, 4) -> [1, 2, 3, 4]
370+ x.a = 'hello'
371+ len(x) -> 4
372+ x.append(5)
373+ len(x) -> 5
374+ x[2] -> 3
375+ x.a -> 'hello'
376+ print x -> [1, 2, 3, 4, 5]
377+ x.__dict__ -> {'a':'hello'}
378+ x.b = [6, 7, 8, 9]
379+ x.b[-1] -> 9
380+ len(x.b) -> 4
381
382-Other valid constructors:
383- x = Lstruct(1, 2, 3, a = 'hello') -> x.a -> 'hello', x -> [1, 2, 3]
384- x = Lstruct(1, 2, 3)(a = 'hello')
385- x = Lstruct([1, 2, 3], x = 'hello')
386- x = Lstruct((1, 2, 3), a = 'hello')
387+ Other valid constructors:
388+ x = Lstruct(1, 2, 3, a = 'hello') -> x.a -> 'hello', x -> [1, 2, 3]
389+ x = Lstruct(1, 2, 3)(a = 'hello')
390+ x = Lstruct([1, 2, 3], x = 'hello')
391+ x = Lstruct((1, 2, 3), a = 'hello')
392
393-Credit: https://github.com/Vectorized/Python-Attribute-List
394-'''
395+ Sources:
396+ -https://github.com/Vectorized/Python-Attribute-List
397+ '''
398
399 def __new__(self, *args, **kwargs):
400 return super(Lstruct, self).__new__(self, args, kwargs)
401@@ -62,36 +64,34 @@
402
403 class OrderedStruct(object):
404 '''
405-A form of dictionary-like structure that maintains the
406- ordering in which its fields/attributes and their
407- corresponding values were added.
408+ A form of dictionary-like structure that maintains the ordering in which
409+ its fields/attributes and their corresponding values were added.
410
411-OrderedDict is a similar device, however this class
412- can be used as an "ordered struct/class" giving
413- it much more flexibility in practice. It is
414+ OrderedDict is a similar device, however this class can be used as an
415+ "ordered struct/class" giving it much more flexibility in practice. It is
416 also easier to work with fixed valued keys in-code.
417
418-Eg:
419-OrderedDict: # a bit clumsy to use and look at
420- x['y'] = 5
421+ Example:
422+ OrderedDict: # a bit clumsy to use and look at
423+ x['y'] = 5
424
425-OrderedStruct: # nicer to look at, and works the same way
426- x.y = 5
427- OR
428- x['y'] = 5 # supports OrderedDict-style usage
429+ OrderedStruct: # nicer to look at, and works the same way
430+ x.y = 5
431+ OR
432+ x['y'] = 5 # supports OrderedDict-style usage
433
434-Supports: len(x), str(x), for-loop iteration.
435-Has methods: x.keys(), x.values(), x.items(), x.iterkeys()
436+ Supports: len(x), str(x), for-loop iteration.
437+ Has methods: x.keys(), x.values(), x.items(), x.iterkeys()
438
439-Usage:
440- x = OrderedStruct()
441- x.y = 5
442- x.z = 6
443- OR
444- x = OrderedStruct('y', 5, 'z', 6)
445+ Usage:
446+ x = OrderedStruct()
447+ x.y = 5
448+ x.z = 6
449+ OR
450+ x = OrderedStruct('y', 5, 'z', 6)
451
452- # note below that the output fields as iterables are always
453- # in the same order as the inputs
454+ note below that the output fields as iterables are always in the same
455+ order as the inputs
456
457 x.keys() -> ['y', 'z']
458 x.values() -> [5, 6]
459@@ -110,16 +110,18 @@
460 ('x', 5)
461 ('y', 6)
462
463- Note: to access internal fields use dir(x)
464- (input fields will be included, but
465- are not technically internals)
466+ Note: to access internal fields use dir(x) (input fields will be included,
467+ but are not technically internals)
468 '''
469
470 def __init__(self, *args):
471- '''Provided either nothing or a series of strings, construct a structure that will,
472-when accessed as a list, return its fields in the same order in which they were provided'''
473+ '''
474+ Provided either nothing or a series of strings, construct a structure
475+ that will, when accessed as a list, return its fields in the same order
476+ in which they were provided
477+ '''
478
479- # keys and values
480+ # keys and values
481 self._k = []
482 self._v = []
483
484@@ -184,9 +186,11 @@
485 yield(a, b)
486
487 def __copy__(self):
488- # shallow copy, hard copies of trivial attributes,
489- # references to structures like lists/OrderedDicts
490- # unless redefined as an entirely different structure
491+ '''
492+ shallow copy, hard copies of trivial attributes,
493+ references to structures like lists/OrderedDicts
494+ unless redefined as an entirely different structure
495+ '''
496 newInstance = type(self)()
497 for k, v in list(self.items()):
498 exec(('newInstance.%s = v') % (k))
499@@ -193,11 +197,13 @@
500 return newInstance
501
502 def __deepcopy__(self, memo=None):
503- # hard copy of all attributes
504- # same thing but call deepcopy recursively
505- # technically not how it should be done,
506- # (see https://docs.python.org/2/library/copy.html #copy.deepcopy )
507- # but will generally work in this case
508+ '''
509+ hard copy of all attributes
510+ same thing but call deepcopy recursively
511+ technically not how it should be done,
512+ (see https://docs.python.org/2/library/copy.html #copy.deepcopy )
513+ but will generally work in this case
514+ '''
515 newInstance = type(self)()
516 for k, v in list(self.items()):
517 exec(('newInstance.%s = deepcopy(v)') % (k))
518@@ -211,7 +217,7 @@
519 i = self._k.index(key)
520 k = self._k.pop(i)
521 v = self._v.pop(i)
522- #exec('del self.%s')%(key)
523+ #exec('del self.%s')%(key)
524 return (k, v)
525
526 def keys(self):
527@@ -226,8 +232,10 @@
528
529 def isempty(x):
530 '''
531- returns true if object is + -infinity, NaN, None, '', has length 0, or is an
532- array/matrix composed only of such components (includes mixtures of "empty" types)'''
533+ returns true if object is +/-infinity, NaN, None, '', has length 0, or is
534+ an array/matrix composed only of such components (includes mixtures of
535+ "empty" types)
536+ '''
537
538 if type(x) in [list, np.ndarray, tuple]:
539 if np.size(x) == 0:
540@@ -261,8 +269,11 @@
541
542
543 def fieldnames(x, ignore_internals=True):
544- '''returns a list of fields of x
545- ignore_internals ignores all fieldnames starting with '_' and is True by default'''
546+ '''
547+ returns a list of fields of x
548+ ignore_internals ignores all fieldnames starting with '_' and is True by
549+ default
550+ '''
551 result = list(vars(x).keys())
552
553 if ignore_internals:
554@@ -272,8 +283,11 @@
555
556
557 def isfield(x, y, ignore_internals=True):
558- '''is y is a field of x?
559- ignore_internals ignores all fieldnames starting with '_' and is True by default'''
560+ '''
561+ is y is a field of x?
562+ ignore_internals ignores all fieldnames starting with '_' and is True by
563+ default
564+ '''
565 return str(y) in fieldnames(x, ignore_internals)
566
567
568@@ -280,7 +294,8 @@
569 def fileparts(x):
570 '''
571 given: "path/path/.../file_name.ext"
572- returns: [path, file_name, ext] (list of strings)'''
573+ returns: [path, file_name, ext] (list of strings)
574+ '''
575 try:
576 a = x[:x.rindex('/')] #path
577 b = x[x.rindex('/') + 1:] #full filename
578@@ -296,17 +311,17 @@
579
580 def fullfile(*args):
581 '''
582- use:
583+ usage:
584+ fullfile(path, path, ... , file_name + ext)
585
586- fullfile(path, path, ... , file_name + ext)
587 returns: "path/path/.../file_name.ext"
588
589 with all arguments as strings with no "/"s
590
591 regarding extensions and the '.':
592- as final arguments ('file.doc') or ('file' + '.doc') will work
593- ('final', '.doc'), and the like, will not (you'd get 'final/.doc')
594-'''
595+ as final arguments ('file.doc') or ('file' + '.doc') will work
596+ ('final', '.doc'), and the like, will not (you'd get 'final/.doc')
597+ '''
598 result = str(args[0])
599 for i in range(len(args[1:])):
600 # if last argument wasn't empty, add a '/' between it and the next argument
601@@ -320,8 +335,11 @@
602 def findline(fidi, s):
603 '''
604 returns full first line containing s (as a string), or None
605+
606 Note: will include any newlines or tabs that occur in that line,
607- use str(findline(f, s)).strip() to remove these, str() in case result is None'''
608+ use str(findline(f, s)).strip() to remove these, str() in case result is
609+ None
610+ '''
611 for line in fidi:
612 if s in line:
613 return line
614@@ -330,20 +348,21 @@
615
616 def empty_nd_list(shape, filler=0., as_numpy_ndarray=False):
617 '''
618-returns a python list of the size/shape given (shape must be int or tuple)
619+ returns a python list of the size/shape given (shape must be int or tuple)
620 the list will be filled with the optional second argument
621
622 filler is 0.0 by default
623
624- as_numpy_ndarray will return the result as a numpy.ndarray and is False by default
625+ as_numpy_ndarray will return the result as a numpy.ndarray and is False by
626+ default
627
628- Note: the filler must be either None/np.nan/float('NaN'), float/double, or int
629- other numpy and float values such as +/- np.inf will also work
630+ Note: the filler must be either None/np.nan/float('NaN'), float/double, or
631+ int. other numpy and float values such as +/- np.inf will also work
632
633-use:
634- empty_nd_list((5, 5), 0.0) # returns a 5x5 matrix of 0.0's
635- empty_nd_list(5, None) # returns a 5 long array of NaN
636-'''
637+ Usage:
638+ empty_nd_list((5, 5), 0.0) # returns a 5x5 matrix of 0.0's
639+ empty_nd_list(5, None) # returns a 5 long array of NaN
640+ '''
641 result = np.empty(shape)
642 result.fill(filler)
643 if not as_numpy_ndarray:
644Index: ../trunk-jpl/src/m/mech/analyticaldamage.py
645===================================================================
646--- ../trunk-jpl/src/m/mech/analyticaldamage.py (revision 25064)
647+++ ../trunk-jpl/src/m/mech/analyticaldamage.py (revision 25065)
648@@ -1,6 +1,6 @@
649 import numpy as np
650+
651 from averaging import averaging
652-#from plotmodel import plotmodel
653 from thomasparams import thomasparams
654
655
656Index: ../trunk-jpl/src/m/plot/applyoptions.m
657===================================================================
658--- ../trunk-jpl/src/m/plot/applyoptions.m (revision 25064)
659+++ ../trunk-jpl/src/m/plot/applyoptions.m (revision 25065)
660@@ -307,7 +307,6 @@
661 end
662 end
663
664-
665 %text (default value is empty, not NaN...)
666 if exist(options,'text');
667 textstring=getfieldvalue(options,'text');
668@@ -351,7 +350,7 @@
669 scaleruler(options);
670 end
671
672-%streamliness
673+%streamlines
674 if exist(options,'streamlines'),
675 plot_streamlines(md,options);
676 end
677Index: ../trunk-jpl/src/m/plot/plot_manager.py
678===================================================================
679--- ../trunk-jpl/src/m/plot/plot_manager.py (revision 25064)
680+++ ../trunk-jpl/src/m/plot/plot_manager.py (revision 25065)
681@@ -1,14 +1,3 @@
682-
683-from checkplotoptions import checkplotoptions
684-from plot_mesh import plot_mesh
685-from plot_BC import plot_BC
686-from plot_elementnumbering import plot_elementnumbering
687-from plot_vertexnumbering import plot_vertexnumbering
688-from processmesh import processmesh
689-from processdata import processdata
690-from plot_unit import plot_unit
691-from applyoptions import applyoptions
692-
693 try:
694 from osgeo import gdal
695 overlaysupport = True
696@@ -16,8 +5,17 @@
697 print('osgeo/gdal for python not installed, overlay plots are not enabled')
698 overlaysupport = False
699
700+from applyoptions import applyoptions
701+from checkplotoptions import checkplotoptions
702+from plot_BC import plot_BC
703+from plot_mesh import plot_mesh
704+from plot_elementnumbering import plot_elementnumbering
705 if overlaysupport:
706 from plot_overlay import plot_overlay
707+from plot_unit import plot_unit
708+from plot_vertexnumbering import plot_vertexnumbering
709+from processdata import processdata
710+from processmesh import processmesh
711
712
713 def plot_manager(md, options, fig, axgrid, gridindex):
714@@ -26,9 +24,12 @@
715
716 'fig' is a handle to the figure instance created by plotmodel.
717
718- 'ax' is a handle to the axes instance created by plotmodel. This is
719+ 'axgrid' is a handle to the axes instance created by plotmodel. This is
720 currently generated using matplotlib's AxesGrid toolkit.
721
722+ 'gridindex' is passed through to specialized plot* functions and
723+ applyoptions.
724+
725 Usage:
726 plot_manager(md, options, fig, ax)
727
728@@ -36,9 +37,14 @@
729 '''
730 #parse options and get a structure of options
731 options = checkplotoptions(md, options)
732+
733 #get data to be plotted
734 data = options.getfieldvalue('data')
735+
736 #add ticklabel has a default option
737+ #
738+ # TODO: Check why we are doing this and if it is absolutely necessary (see
739+ # also src/m/plot/plot_mesh.py, src/m/plot/applyoptions.py)
740 options.addfielddefault('ticklabels', 'on')
741
742 ax = axgrid[gridindex]
743@@ -57,8 +63,7 @@
744 if isinstance(data, str):
745 if data == 'mesh':
746 plot_mesh(md, options, fig, axgrid, gridindex)
747-
748- #fig.delaxes(fig.axes[1]) # hack to remove colorbar after the fact
749+ #fig.delaxes(fig.axes[1]) # hack to remove colorbar after the fact
750 return
751 elif data == 'BC':
752 plot_BC(md, options, fig, axgrid, gridindex)
753Index: ../trunk-jpl/src/m/plot/plot_overlay.py
754===================================================================
755--- ../trunk-jpl/src/m/plot/plot_overlay.py (revision 25064)
756+++ ../trunk-jpl/src/m/plot/plot_overlay.py (revision 25065)
757@@ -1,24 +1,31 @@
758-import numpy as np
759-from processmesh import processmesh
760-from processdata import processdata
761-from xy2ll import xy2ll
762+import os
763+
764 import matplotlib.pyplot as plt
765 import matplotlib as mpl
766-import os
767 try:
768 from mpl_toolkits.basemap import Basemap
769 except ImportError:
770 print('Basemap toolkit not installed')
771+import numpy as np
772 try:
773 from osgeo import gdal
774 except ImportError:
775 print('osgeo/gdal for python not installed, plot_overlay is disabled')
776
777+from processdata import processdata
778+from processmesh import processmesh
779+from xy2ll import xy2ll
780
781+
782 def plot_overlay(md, data, options, ax):
783 '''
784- Function for plotting a georeferenced image. This function is called
785- from within the plotmodel code.
786+ Function for plotting a georeferenced image. Called from plot_manager by
787+ call to plotmodel.
788+
789+ Usage:
790+ plot_overlay(md, data, options, ax)
791+
792+ See also: PLOTMODEL
793 '''
794
795 x, y, z, elements, is2d, isplanet = processmesh(md, [], options)
796@@ -80,7 +87,7 @@
797 plt.hist(arr.flatten(), bins=256, range=(0., 1.))
798 plt.title('histogram of overlay image, use for setting overlaylims')
799 plt.show()
800- plt.sca(ax) # return to original axes / figure
801+ plt.sca(ax) # return to original axes/figure
802
803 # get parameters from cropped geotiff
804 trans = gtif.GetGeoTransform()
805Index: ../trunk-jpl/src/m/plot/plotmodel.m
806===================================================================
807--- ../trunk-jpl/src/m/plot/plotmodel.m (revision 25064)
808+++ ../trunk-jpl/src/m/plot/plotmodel.m (revision 25065)
809@@ -9,7 +9,7 @@
810 numberofplots=options.numberofplots;
811
812 %get number of subplots
813-subplotwidth=ceil(sqrt(options.numberofplots));
814+subplotwidth=ceil(sqrt(numberofplots));
815
816 %if nlines and ncols specified, then bypass.
817 if ~exist(options.list{1},'nlines') & ~exist(options.list{1},'ncols')
818@@ -103,7 +103,7 @@
819 end
820 end % }}}
821
822- %Use zbuffer renderer (snoother colors) and white background
823+ %Use zbuffer renderer (smoother colors) and white background
824 set(f,'Renderer','zbuffer','color',getfieldvalue(options.list{1},'figurebackgroundcolor','w'));
825
826 %Go through all data plottable and close window if an error occurs
827@@ -124,5 +124,5 @@
828 rethrow(me);
829 end
830 else
831- error('plotmodel error message: no output data found. ');
832+ error('plotmodel error message: no output data found.');
833 end
834Index: ../trunk-jpl/src/m/plot/plot_manager.m
835===================================================================
836--- ../trunk-jpl/src/m/plot/plot_manager.m (revision 25064)
837+++ ../trunk-jpl/src/m/plot/plot_manager.m (revision 25065)
838@@ -14,9 +14,7 @@
839
840 %figure out if this is a special plot
841 if ischar(data),
842-
843 switch data,
844-
845 case 'boundaries',
846 plot_boundaries(md,options,subplotwidth,i);
847 return;
848@@ -32,23 +30,18 @@
849 case 'highlightelements',
850 plot_highlightelements(md,options,subplotwidth,i);
851 return;
852-
853 case 'qmumean',
854 plot_qmumean(md,options,nlines,ncols,i);
855 return;
856-
857 case 'qmustddev',
858 plot_qmustddev(md,options,nlines,ncols,i);
859 return;
860-
861 case 'qmuhistnorm',
862 plot_qmuhistnorm(md,options,nlines,ncols,i);
863 return;
864-
865 case 'qmu_mass_flux_segments',
866 plot_qmu_mass_flux_segments(md,options,nlines,ncols,i);
867 return;
868-
869 case 'part_hist',
870 plot_parthist(md,options,nlines,ncols,i);
871 return;
872@@ -120,10 +113,8 @@
873 case 'segments'
874 plot_segments(md,options,subplotwidth,i,data)
875 return
876-
877 case 'quiver'
878 data=[md.initialization.vx md.initialization.vy]; %Go ahead and try plot_unit
879-
880 case {'strainrate_tensor','strainrate','strainrate_principal','strainrate_principalaxis1','strainrate_principalaxis2','strainrate_principalaxis3',...
881 'stress_tensor','stress','stress_principal','stress_principalaxis1','stress_principalaxis2','stress_principalaxis3',...
882 'deviatoricstress_tensor','deviatoricstress','deviatoricstress_principal','deviatoricstress_principalaxis1','deviatoricstress_principalaxis2','deviatoricstress_principalaxis3'},
883@@ -137,13 +128,10 @@
884 return;
885 case 'transient_results',
886 plot_transient_results(md,options,subplotwidth,i);
887-
888 case 'transient_field',
889 plot_transient_field(md,options,subplotwidth,i);
890 return;
891-
892 otherwise,
893-
894 if ismember(data,properties('model')),
895 data=eval(['md.' data ';']);
896 else
897@@ -158,19 +146,19 @@
898 return;
899 end
900
901-%Figure out if this is a semi-transparent plot.
902+%Figure out if this is a Google Maps plot.
903 if exist(options,'googlemaps'),
904 plot_googlemaps(md,data,options,nlines,ncols,i);
905 return;
906 end
907
908-%Figure out if this is a semi-transparent plot.
909+%Figure out if this is a landsat plot.
910 if getfieldvalue(options,'landsat',0),
911 plot_landsat(md,data,options,nlines,ncols,i);
912 return;
913 end
914
915-%Figure out if this is a semi-transparent plot.
916+%Figure out if this is a gridded plot.
917 if exist(options,'gridded'),
918 plot_gridded(md,data,options,nlines,ncols,i);
919 return;
920Index: ../trunk-jpl/src/m/shp/shpread.py
921===================================================================
922--- ../trunk-jpl/src/m/shp/shpread.py (nonexistent)
923+++ ../trunk-jpl/src/m/shp/shpread.py (revision 25065)
924@@ -0,0 +1,136 @@
925+from os import path
926+try:
927+ import shapefile
928+except ImportError:
929+ print("could not import shapefile, PyShp has not been installed, no shapefile reading capabilities enabled")
930+
931+from pairoptions import pairoptions
932+
933+
934+def shpread(filename, *args): #{{{
935+ '''
936+ SHPREAD - read a shape file and build a dict
937+
938+ This routine reads a shape file .shp and builds a dict array containing the
939+ fields x and y corresponding to the coordinates, one for the filename
940+ of the shp file, for the density, for the nodes, and a field closed to
941+ indicate if the domain is closed. If this initial shapefile is point only,
942+ the fields closed and points are ommited.
943+ The first argument is the .shp file to be read and the second one
944+ (optional) indicates if the last point shall be read (1 to read it, 0 not
945+ to).
946+
947+ The current implementation of shpread depends on PyShp.
948+
949+ Usage:
950+ dict = shpread(filename)
951+
952+ Example:
953+ From underling PyShp implementation, "The shapefile format is actually
954+ a collection of three files. You specify the base filename of the
955+ shapefile or the complete filename of any of the shapefile component
956+ files.""
957+
958+ dict = shpread('domainoutline.shp')
959+ OR
960+ dict = shpread('domainoutline.dbf')
961+ OR
962+ dict = shpread('domainoutline')
963+
964+ "OR any of the other 5+ formats which are potentially part of a
965+ shapefile. The library does not care about file extensions". We do,
966+ however, check that a file with the base filename or base filename with
967+ .shp extension exists.
968+
969+ See also EXPDOC, EXPWRITEASVERTICES
970+
971+ Sources:
972+ - https://github.com/GeospatialPython/pyshp
973+ '''
974+
975+ #recover options
976+ options = pairoptions(*args)
977+
978+ #some checks
979+ if not (path.isfile(filename) or path.isfile(filename + '.shp')):
980+ raise RuntimeError('shpread error message: file {} or {}.shp not found!'.format(filename, filename))
981+
982+ #read shapefile
983+ sf = shapefile.Reader(filename)
984+
985+ Dicts = []
986+
987+ #retrieve ID (if it exists)
988+ fields = sf.fields
989+ for i in range(1, len(fields)): # skip over first field, which is "DeletionFlag"
990+ if fields[i][0] == 'id':
991+ name = str(sf.record(i - 1)[0]) # record index is offset by one, again, because of "DeletionFlag"
992+ break
993+
994+ shapes = sf.shapes()
995+ for i in range(len(shapes)):
996+ Dict = {}
997+ shape = shapes[i]
998+ if shape.shapeType == shapefile.POINT:
999+ Dict['x'] = shape.points[0][0]
1000+ Dict['y'] = shape.points[0][1]
1001+ Dict['density'] = 1
1002+ Dict['Geometry'] = 'Point'
1003+ elif shape.shapeType == shapefile.POLYLINE:
1004+ num_points = len(shape.points)
1005+ x = []
1006+ y = []
1007+ for j in range(num_points):
1008+ point = shape.points[j]
1009+ x.append(point[0])
1010+ y.append(point[1])
1011+ Dict['x'] = x
1012+ Dict['y'] = y
1013+ Dict['nods'] = num_points
1014+ Dict['density'] = 1
1015+ Dict['closed'] = 1
1016+ Dict['BoundingBox'] = shape.bbox
1017+ Dict['Geometry'] = 'Line'
1018+ elif shape.shapeType == shapefile.POLYGON:
1019+ num_points = len(shape.points)
1020+ x = []
1021+ y = []
1022+ for j in range(num_points):
1023+ point = shape.points[j]
1024+ x.append(point[0])
1025+ y.append(point[1])
1026+ Dict['x'] = x
1027+ Dict['y'] = y
1028+ Dict['nods'] = num_points
1029+ Dict['density'] = 1
1030+ Dict['closed'] = 1
1031+ Dict['BoundingBox'] = shape.bbox
1032+ Dict['Geometry'] = 'Polygon'
1033+ else:
1034+ # NOTE: We could do this once before looping over shapes as all
1035+ # shapes in the file must be of the same type, but we would
1036+ # need to have a second check anyway in order to know how to
1037+ # parse the points. So, let's just assume the file is not
1038+ # malformed.
1039+ #
1040+ raise Exception('shpread error: geometry {} is not currently supported'.format(shape.shapeTypeName))
1041+ name = ''
1042+ fields = sf.fields
1043+ for j in range(1, len(fields)): # skip over first field, which is "DeletionFlag"
1044+ fieldname = fields[j][0]
1045+ # 'id' field gets special treatment
1046+ if fieldname == 'id':
1047+ name = str(sf.record(j - 1)[0]) # record index is offset by one, again, because of "DeletionFlag"
1048+ else:
1049+ Dict[fieldname] = sf.record(j - 1)[0]
1050+ Dict['name'] = name
1051+ Dicts.append(Dict)
1052+
1053+ invert = options.getfieldvalue('invert', 0)
1054+ if invert:
1055+ for i in range(len(Dicts)):
1056+ Dicts[i].x = np.flipud(Dicts[i].x)
1057+ Dicts[i].y = np.flipud(Dicts[i].y)
1058+
1059+ return Dicts
1060+#}}}
1061Index: ../trunk-jpl/src/m/shp/shpread.m
1062===================================================================
1063--- ../trunk-jpl/src/m/shp/shpread.m (revision 25064)
1064+++ ../trunk-jpl/src/m/shp/shpread.m (revision 25065)
1065@@ -1,22 +1,22 @@
1066 function Struct=shpread(filename,varargin)
1067-%SHPREAD - read a shape file and build a Structure
1068+%SHPREAD - read a shape file and build a struct
1069 %
1070-% This routine reads a shape file .shp and builds a Structure containing the
1071-% fields x and y corresponding to the coordinates, one for the filename of
1072-% the shp file, for the density, for the nodes, and a field closed to
1073-% indicate if the domain is closed.
1074-% If this initial shapefile is point only, the fields closed and
1075-% points are ommited
1076-% The first argument is the .shp file to be read and the second one (optional)
1077-% indicates if the last point shall be read (1 to read it, 0 not to).
1078+% This routine reads a shape file .shp and builds a structure array
1079+% containing the fields x and y corresponding to the coordinates, one for the
1080+% filename of the shp file, for the density, for the nodes, and a field
1081+% closed to indicate if the domain is closed. If this initial shapefile is
1082+% point only, the fields closed and points are omitted.
1083+% The first argument is the .shp file to be read and the second one
1084+% (optional) indicates if the last point shall be read (1 to read it, 0 not
1085+% to).
1086 %
1087-% Usage:
1088-% Struct=shpread(filename)
1089+% Usage:
1090+% Struct=shpread(filename)
1091 %
1092-% Example:
1093-% Struct=shpread('domainoutline.shp')
1094+% Example:
1095+% Struct=shpread('domainoutline.shp')
1096 %
1097-% See also EXPDOC, EXPWRITEASVERTICES
1098+% See also EXPDOC, EXPWRITEASVERTICES
1099
1100 %recover options
1101 options=pairoptions(varargin{:});
1102@@ -26,9 +26,6 @@
1103 error(['shpread error message: file ' filename ' not found!']);
1104 end
1105
1106-%initialize number of profile
1107-count=0;
1108-
1109 %read shapefile
1110 shp=shaperead(filename);
1111
1112@@ -35,7 +32,7 @@
1113 Struct=struct([]);
1114 fields=fieldnames(shp);
1115 for i=1:length(shp),
1116- if strcmpi(shp(i).Geometry,'Polygon'),
1117+ if strcmpi(shp(i).Geometry,'Point'),
1118 x=shp(i).X'; y=shp(i).Y';
1119 ids=find(isnan(x));
1120 x(ids)=[]; y(ids)=[];
1121@@ -42,9 +39,7 @@
1122
1123 Struct(end+1).x=x;
1124 Struct(end).y=y;
1125- Struct(end).nods=length(x);
1126 Struct(end).density=1;
1127- Struct(end).closed=1;
1128 if isfield(shp,'id'),
1129 Struct(end).name=num2str(shp(i).id);
1130 else
1131@@ -56,9 +51,7 @@
1132 Struct(end).(field)=shp(i).(field);
1133 end
1134 end
1135- end
1136-
1137- if strcmpi(shp(i).Geometry,'Line'),
1138+ elseif strcmpi(shp(i).Geometry,'Line'),
1139 x=shp(i).X'; y=shp(i).Y';
1140 ids=find(isnan(x));
1141 x(ids)=[]; y(ids)=[];
1142@@ -79,10 +72,7 @@
1143 Struct(end).(field)=shp(i).(field);
1144 end
1145 end
1146- end
1147-
1148-
1149- if strcmpi(shp(i).Geometry,'Point'),
1150+ elseif strcmpi(shp(i).Geometry,'Polygon'),
1151 x=shp(i).X'; y=shp(i).Y';
1152 ids=find(isnan(x));
1153 x(ids)=[]; y(ids)=[];
1154@@ -89,7 +79,9 @@
1155
1156 Struct(end+1).x=x;
1157 Struct(end).y=y;
1158+ Struct(end).nods=length(x);
1159 Struct(end).density=1;
1160+ Struct(end).closed=1;
1161 if isfield(shp,'id'),
1162 Struct(end).name=num2str(shp(i).id);
1163 else
1164Index: ../trunk-jpl/src/m/mesh/planet/gmsh/gmshplanet.py
1165===================================================================
1166--- ../trunk-jpl/src/m/mesh/planet/gmsh/gmshplanet.py (revision 25064)
1167+++ ../trunk-jpl/src/m/mesh/planet/gmsh/gmshplanet.py (revision 25065)
1168@@ -1,4 +1,3 @@
1169-import os
1170 import subprocess
1171
1172 import numpy as np
1173@@ -9,7 +8,7 @@
1174 from pairoptions import *
1175
1176
1177-def gmshplanet(*varargin):
1178+def gmshplanet(*args):
1179 '''
1180 GMSHPLANET - mesh generation for a sphere. Very specific code for gmsh from $ISSM_DIR/src/demos/simple_geo/sphere.geo
1181
1182@@ -33,7 +32,7 @@
1183 raise RuntimeError('gmshplanet: Gmsh major version %s not supported!' % gmshmajorversion)
1184
1185 #process options
1186- options = pairoptions(*varargin)
1187+ options = pairoptions(*args)
1188 #options = deleteduplicates(options, 1)
1189
1190 #recover parameters:
1191Index: ../trunk-jpl/src/m/coordsystems/gdaltransform.py
1192===================================================================
1193--- ../trunk-jpl/src/m/coordsystems/gdaltransform.py (revision 25064)
1194+++ ../trunk-jpl/src/m/coordsystems/gdaltransform.py (revision 25065)
1195@@ -5,31 +5,35 @@
1196
1197 from loadvars import *
1198
1199+
1200 def gdaltransform(x, y, proj_in, proj_out): #{{{
1201 '''
1202 GDALTRANSFORM - switch from one projection system to another
1203
1204- Usage:
1205- [x, y] = gdaltransform(x1, y1, epsg_in, epsg_out)
1206+ Usage:
1207+ [x, y] = gdaltransform(x1, y1, epsg_in, epsg_out)
1208
1209- Example:
1210- [x, y] = gdaltranform(md.mesh.long, md.mesh.lat, 'EPSG:4326', 'EPSG:3031')
1211+ Example:
1212+ [x, y] = gdaltranform(md.mesh.long, md.mesh.lat, 'EPSG:4326', 'EPSG:3031')
1213
1214- For reference:
1215- EPSG: 4326 (lat, long)
1216- EPSG: 3341 (Greenland, UPS 45W, 70N)
1217- EPSG: 3031 (Antarctica, UPS 0E, 71S)
1218+ For reference:
1219+ EPSG: 4326 (lat, long)
1220+ EPSG: 3341 (Greenland, UPS 45W, 70N)
1221+ EPSG: 3031 (Antarctica, UPS 0E, 71S)
1222
1223- ll2xy default projection Antarctica:
1224- +proj = stere +lat_0=-90 +lat_ts=-71 +lon_0=0 +k=1 +x_0=0 +y_0=0 +a=6378273 +b=6356889.448564109 +units=m +no_defs
1225- ll2xy default projection Greenland:
1226- +proj = stere +lat_0=90 +lat_ts=70 +lon_0=-45 +k=1 +x_0=0 +y_0=0 +a=6378273 +b=6356889.448564109 +units=m +no_defs
1227- Bamber's Greenland projection
1228- +proj = stere +lat_0=90 +lat_ts=71 +lon_0=-39 +k=1 +x_0=0 +y_0=0 +a=6378273 +b=6356889.448564109 +units=m +no_defs
1229+ ll2xy default projection Antarctica:
1230+ +proj = stere +lat_0=-90 +lat_ts=-71 +lon_0=0 +k=1 +x_0=0 +y_0=0 +a=6378273 +b=6356889.448564109 +units=m +no_defs
1231+ ll2xy default projection Greenland:
1232+ +proj = stere +lat_0=90 +lat_ts=70 +lon_0=-45 +k=1 +x_0=0 +y_0=0 +a=6378273 +b=6356889.448564109 +units=m +no_defs
1233+ Bamber's Greenland projection
1234+ +proj = stere +lat_0=90 +lat_ts=71 +lon_0=-39 +k=1 +x_0=0 +y_0=0 +a=6378273 +b=6356889.448564109 +units=m +no_defs
1235
1236- To get proj.4 string form EPSG, use gdalsrsinfo. Example:
1237+ To get proj.4 string form EPSG, use gdalsrsinfo. Example:
1238
1239- gdalsrsinfo epsg:4326 | grep "PROJ.4" | sed "s/PROJ.4 : //"
1240+ gdalsrsinfo epsg:4326 | grep "PROJ.4" | sed "s/PROJ.4 : //"
1241+
1242+ TODO:
1243+ - Remove shlex and follow subprocess pattern implemented in src/m/coordsystems/epsg2proj.py
1244 '''
1245
1246 # Give ourselves unique file names
1247@@ -41,7 +45,7 @@
1248 file_in.write(b'%8g %8g\n' % (x.flatten(1) y.flatten(1)))
1249 file_in.close()
1250
1251- args = shlex.split('gdaltransform -s_srs %s -t_srs %s < %s > %s' % (proj_in, proj_out, filename_in, filename_out))
1252+ args = shlex.split('gdaltransform -s_srs {} -t_srs {} < {} > {}'.format(proj_in, proj_out, filename_in, filename_out))
1253 subprocess.check_call(args) # Will raise CalledProcessError if return code is not 0
1254
1255 A = loadvars(filename_out)
1256@@ -52,4 +56,6 @@
1257
1258 os.remove(filename_in)
1259 os.remove(filename_out)
1260+
1261+ return [xout, yout]
1262 #}}}
1263Index: ../trunk-jpl/src/m/geometry/polyarea.py
1264===================================================================
1265--- ../trunk-jpl/src/m/geometry/polyarea.py (nonexistent)
1266+++ ../trunk-jpl/src/m/geometry/polyarea.py (revision 25065)
1267@@ -0,0 +1,28 @@
1268+import math
1269+
1270+import numpy as np
1271+
1272+
1273+def polyarea: #{{{
1274+ '''
1275+ POLYAREA - returns the area of the 2-D polygon defined by the vertices in
1276+ lists x and y
1277+
1278+ Partial implementation of MATLAB's polyarea function. If x and y are
1279+ lists of the same length, then polyarea returns the scalar area of the
1280+ polygon defined by x and y.
1281+
1282+ Usage:
1283+ a = polyarea(x, y)
1284+
1285+ Sources:
1286+ - https://www.mathworks.com/help/matlab/ref/polyarea.html
1287+ - https://stackoverflow.com/questions/24467972/calculate-area-of-polygon-given-x-y-coordinates
1288+ - https://en.wikipedia.org/wiki/Shoelace_formula
1289+
1290+ TODO:
1291+ - Test that output falls within some tolerance of MATLAB's polyarea
1292+ function.
1293+ '''
1294+ return 0.5 * np.abs(np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1)))
1295+#}}}
1296Index: ../trunk-jpl/src/m/classes/boundary.py
1297===================================================================
1298--- ../trunk-jpl/src/m/classes/boundary.py (nonexistent)
1299+++ ../trunk-jpl/src/m/classes/boundary.py (revision 25065)
1300@@ -0,0 +1,176 @@
1301+import math
1302+
1303+import numpy as np
1304+
1305+from epsg2proj import epsg2proj
1306+from pairoptions import pairoptions
1307+from shpread import shpread
1308+
1309+
1310+class boundary(object): #{{{
1311+ '''
1312+ BOUNDARY class definition
1313+
1314+ Usage:
1315+ boundary = boundary()
1316+ '''
1317+
1318+ def __init__(self, *args): #{{{
1319+ self.boundaries = []
1320+ self.name = ''
1321+ self.continent = ''
1322+ self.proj = epsg2proj(4326)
1323+
1324+ self.setdefaultparameters()
1325+
1326+ if len(args):
1327+ options = pairoptions(*args)
1328+
1329+ #recover field values
1330+ self.shppath = options.getfieldvalue('shppath', '')
1331+ self.shpfilename = options.getfieldvalue('shpfilename', '')
1332+ self.orientation = options.getfieldvalue('orientation', 'normal')
1333+
1334+ #figure out projection string:
1335+ if options.exist('epsg'):
1336+ if options.exist('proj'):
1337+ raise RuntimeError('Error in constructor for boundary {}. Cannot supply epsg and proj at the same time!'.format(self.shppath))
1338+ epsg = options.getfieldvalue('epsg')
1339+ proj = epsg2proj(epsg) # convert to PROJ.4 format
1340+ elif options.exist('proj'):
1341+ if options.exist('epsg'):
1342+ raise RuntimeError('Error in constructor for boundary {}. Cannot supply proj and epsg at the same time!'.format(self.shppath))
1343+ proj = options.getfieldvalue('proj')
1344+ else:
1345+ proj = '+proj=longlat +datum=WGS84 +no_defs'
1346+
1347+ self.proj = proj
1348+ #}}}
1349+
1350+ def __repr__(self): #{{{
1351+ s = ' boundary parameters:\n'
1352+ s += '{}\n'.format(fielddisplay(self, 'shppath', 'path to filename for this boundary'))
1353+ s += '{}\n'.format(fielddisplay(self, 'shpfilename', 'shape file name'))
1354+ s += '{}\n'.format(fielddisplay(self, 'orientation', 'orientation (default is \'normal\', can be \'reverse\')'))
1355+ s += '{}\n'.format(fielddisplay(self, 'proj', 'shape file projection string (follows PROJ.4 standard)'))
1356+
1357+ return s
1358+ #}}}
1359+
1360+ def setdefaultparameters(self): #{{{
1361+ self.shppath = ''
1362+ self.shpfilename = ''
1363+ self.orientation = 'normal'
1364+ self.proj = '+proj=longlat +datum=WGS84 +no_defs' #EPSG 4326
1365+
1366+ return self
1367+ #}}}
1368+
1369+ def name(self): #{{{
1370+ output = self.shpfilename
1371+
1372+ return output
1373+ #}}}
1374+
1375+ def edges(self): #{{{
1376+ #read domain
1377+ path, name, ext = fileparts(shp.shpfilename)
1378+ if ext != 'shp':
1379+ ext = 'shp'
1380+ output = shpread('{}/{}.{}'.format(self.shppath, name, ext))
1381+
1382+ #do we reverse?
1383+ if self.orientation == 'reverse':
1384+ output.x = np.flipud(output.x)
1385+ output.y = np.flipud(output.y)
1386+ #}}}
1387+
1388+ def plot(self, *args): #{{{
1389+ #recover options
1390+ options = pairoptions(*args)
1391+
1392+ #parse input
1393+ # TODO: Sort out what options we should set (see
1394+ # src/m/classes/boundary.m)
1395+
1396+ if options.exist('epsg'):
1397+ if options.exist('proj'):
1398+ raise RuntimeError('Boundary plot error message: cannot specify epsg and proj at the same time for plot projection')
1399+ proj = epsg2proj(options.getfieldvalue('epsg'))
1400+ elif options.exist('proj'):
1401+ proj = options.getfieldvalue('proj')
1402+ else:
1403+ proj = epsg2proj(4326)
1404+
1405+ #read domain
1406+ path, name, ext = fileparts(shp.shpfilename)
1407+ if ext != 'shp':
1408+ ext = 'shp'
1409+ domain = shpread('{}/{}.{}'.format(self.shppath, name, ext))
1410+
1411+ #convert boundary to another reference frame #{{{
1412+ for i in range(len(domain)):
1413+ try:
1414+ x, y = gdaltransform(domain[i].x, domain[i].y, self.proj, proj)
1415+ except error as e:
1416+ print(e)
1417+ print(self)
1418+
1419+ # TODO: Figure out how to recover figure here: do we pass 'fig' and
1420+ # 'ax' in args?
1421+ #for i in range(len(domain)):
1422+ # x = domain[i].x * unitmultiplier
1423+ # y = domain[i].y * unitmultiplier
1424+ # if len(x) == 1:
1425+ #}}}
1426+
1427+ #TODO: Finish translating from MATLAB after test2004.py runs without plot
1428+ #}}}
1429+
1430+ def checkconsistency(self, *args): #{{{
1431+ #recover options
1432+ options = pairoptions(*args)
1433+ tolerance = getfieldvalue(options, 'tolerance', 1e-5)
1434+
1435+ #recover edges
1436+ edges = self.edges()
1437+
1438+ if edges.Geometry == 'Point':
1439+ return
1440+ else:
1441+ #check that we don't have two identical vertices
1442+ x = edges.x
1443+ y = edges.y
1444+ distance = math.sqrt((x[:-2] - x[1:-1]) ** 2 + (y[:-2] - y[1:-1]) ** 2)
1445+ dmax = distance.max()
1446+ isidentical = np.where(np.asarray(distance) < dmax * tolerance)
1447+ for elem in isidentical: # distance should be a 1D array, but return from np.where is a tuple of arrays
1448+ if len(elem) != 0:
1449+ raise Exception('boundary {} has two vertices extermely close to one another'.format(shpfilename))
1450+
1451+ def plot3d(self, *args): #{{{
1452+ #recover options
1453+ options = pairoptions(*args)
1454+
1455+ #parse input
1456+ # TODO: Sort out what options we should set (see
1457+ # src/m/classes/boundary.m)
1458+
1459+ if options.exist('epsg'):
1460+ if options.exist('proj'):
1461+ raise RuntimeError('Boundary plot error message: cannot specify epsg and proj at the same time for plot projection')
1462+ proj = epsg2proj(options.getfieldvalue('epsg'))
1463+ elif options.exist('proj'):
1464+ proj = options.getfieldvalue('proj')
1465+ else:
1466+ proj = epsg2proj(4326)
1467+
1468+ #read domain
1469+ path, name, ext = fileparts(shp.shpfilename)
1470+ if ext != 'shp':
1471+ ext = 'shp'
1472+ domain = shpread('{}/{}.{}'.format(self.shppath, name, ext))
1473+
1474+ #TODO: Finish translating from MATLAB after test2004.py runs without plot
1475+ #}}}
1476+#}}}
1477Index: ../trunk-jpl/src/m/classes/sealevelmodel.m
1478===================================================================
1479--- ../trunk-jpl/src/m/classes/sealevelmodel.m (revision 25064)
1480+++ ../trunk-jpl/src/m/classes/sealevelmodel.m (revision 25065)
1481@@ -162,10 +162,10 @@
1482 list=unique(list);
1483 end % }}}
1484 function addbasin(self,bas) % {{{
1485- if ~strcmpi(class(bas),'basin')
1486- error('addbasin method only takes a ''basin'' class object as input');
1487- end;
1488- self.basins{end+1}=bas;
1489+ if ~strcmpi(class(bas),'basin')
1490+ error('addbasin method only takes a ''basin'' class object as input');
1491+ end;
1492+ self.basins{end+1}=bas;
1493 end % }}}
1494 function intersections(self,varargin) % {{{
1495
1496Index: ../trunk-jpl/src/m/classes/sealevelmodel.py
1497===================================================================
1498--- ../trunk-jpl/src/m/classes/sealevelmodel.py (revision 25064)
1499+++ ../trunk-jpl/src/m/classes/sealevelmodel.py (revision 25065)
1500@@ -10,21 +10,22 @@
1501 from private import private
1502 from TwoDToThreeD import TwoDToThreeD
1503
1504+
1505 class sealevelmodel(object):
1506 '''
1507 SEALEVELMODEL class definition
1508
1509- Usage:
1510- slm = sealevelmodel(*args)
1511+ Usage:
1512+ slm = sealevelmodel(*args)
1513
1514- where args is a variable list of options
1515+ where args is a variable list of options
1516
1517- Example:
1518- slm = sealevelmodel(
1519- 'icecap', md_greenland,
1520- 'icecap', md_antarctica,
1521- 'earth', md_earth
1522- )
1523+ Example:
1524+ slm = sealevelmodel(
1525+ 'icecap', md_greenland,
1526+ 'icecap', md_antarctica,
1527+ 'earth', md_earth
1528+ )
1529 '''
1530
1531 def __init__(self, *args): #{{{
1532@@ -58,11 +59,11 @@
1533 #}}}
1534
1535 def __repr__(self): # {{{
1536- s = '%s\n' % fielddisplay(self, 'icecaps', 'ice caps')
1537- s += '%s\n' % fielddisplay(self, 'earth', 'earth')
1538- s += '%s\n' % fielddisplay(self, 'settings', 'settings properties')
1539- s += '%s\n' % fielddisplay(self, 'cluster', 'cluster parameters (number of cpus...')
1540- s += '%s\n' % fielddisplay(self, 'miscellaneous', 'miscellaneous fields')
1541+ s = '{}\n'.format(fielddisplay(self, 'icecaps', 'ice caps'))
1542+ s += '{}\n'.format(fielddisplay(self, 'earth', 'earth'))
1543+ s += '{}\n'.format(fielddisplay(self, 'settings', 'settings properties'))
1544+ s += '{}\n'.format(fielddisplay(self, 'cluster', 'cluster parameters (number of cpus...'))
1545+ s += '{}\n'.format(fielddisplay(self, 'miscellaneous', 'miscellaneous fields'))
1546 #}}}
1547
1548 def setdefaultparameters(self): # {{{
1549@@ -84,7 +85,7 @@
1550 # Is the coupler turned on?
1551 for i in range(len(slm.icecaps)):
1552 if slm.icecaps[i].transient.iscoupler == 0:
1553- warnings.warn('sealevelmodel checkconsistency error: icecap model %s should have the transient coupler option turned on!' % slm.icecaps[i].miscellaneous.name)
1554+ warnings.warn('sealevelmodel checkconsistency error: icecap model {} should have the transient coupler option turned on!'.format(slm.icecaps[i].miscellaneous.name))
1555
1556 if slm.earth.transient.iscoupler == 0:
1557 warnings.warn('sealevelmodel checkconsistency error: earth model should have the transient coupler option turned on!')
1558@@ -92,18 +93,18 @@
1559 # Check that the transition vectors have the right size
1560 for i in range(len(slm.icecaps)):
1561 if slm.icecaps[i].mesh.numberofvertices != len(slm.earth.slr.transitions[i]):
1562- raise RuntimeError('sealevelmodel checkconsistency issue with size of transition vector for ice cap: %i name: %s' % (i, slm.icecaps[i].miscellaneous.name))
1563+ raise RuntimeError('sealevelmodel checkconsistency issue with size of transition vector for ice cap: {} name: {}'.format(i, slm.icecaps[i].miscellaneous.name))
1564
1565 # Check that run frequency is the same everywhere
1566 for i in range(len(slm.icecaps)):
1567 if slm.icecaps[i].slr.geodetic_run_frequency != slm.earth.geodetic_run_frequency:
1568- raise RuntimeError('sealevelmodel checkconsistency error: icecap model %s should have the same run frequency as earth!' % slm.icecaps[i].miscellaneous.name)
1569+ raise RuntimeError('sealevelmodel checkconsistency error: icecap model {} should have the same run frequency as earth!'.format(slm.icecaps[i].miscellaneous.name))
1570
1571 # Make sure steric_rate is the same everywhere
1572 for i in range(len(slm.icecaps)):
1573 md = slm.icecaps[i]
1574 if np.nonzero(md.slr.steric_rate - slm.earth.slr.steric_rate[slm.earth.slr.transitions[i]]) != []:
1575- raise RuntimeError('steric rate on ice cap %s is not the same as for the earth' % md.miscellaneous.name)
1576+ raise RuntimeError('steric rate on ice cap {} is not the same as for the earth'.format(md.miscellaneous.name))
1577 #}}}
1578
1579 def mergeresults(self): # {{{
1580@@ -137,7 +138,7 @@
1581
1582 def listcaps(self): # {{{
1583 for i in range(len(self.icecaps)):
1584- print('%i: %s' % (i, self.icecaps[i].miscellaneous.name))
1585+ print('{}: {}'.format(i, self.icecaps[i].miscellaneous.name))
1586 #}}}
1587
1588 def continents(self): # {{{
1589@@ -184,7 +185,7 @@
1590 yei = mdi.mesh.y[mdi.mesh.elements] * onesmatrix / 3
1591 zei = mdi.mesh.z[mdi.mesh.elements] * onesmatrix / 3
1592
1593- print('Computing vertex intersections for basin %s' % self.basins[i].name)
1594+ print('Computing vertex intersections for basin {}'.format(self.basins[i].name))
1595
1596 self.transitions.append(meshintersect3d(self.earth.mesh.x, self.earth.mesh.y, self.earth.mesh.z, mdi.mesh.x, mdi.mesh.y, mdi.mesh.z, 'force', force))
1597 self.eltransitions.append(meshintersect3d(xe, ye, ze, xei, yei, zei, 'force', force))
1598Index: ../trunk-jpl/src/m/classes/basin.py
1599===================================================================
1600--- ../trunk-jpl/src/m/classes/basin.py (nonexistent)
1601+++ ../trunk-jpl/src/m/classes/basin.py (revision 25065)
1602@@ -0,0 +1,214 @@
1603+import math
1604+
1605+from boundary import boundary
1606+from epsg2proj import epsg2proj
1607+from helpers import fileparts
1608+from laea import laea
1609+from pairoptions import pairoptions
1610+from polyarea import polyarea
1611+from shpread import shpread
1612+
1613+
1614+class basin(object): #{{{
1615+ '''
1616+ BASIN class definition
1617+
1618+ Usage:
1619+ basin = basin()
1620+ '''
1621+
1622+ def __init__(self, *args): #{{{
1623+ self.boundaries = []
1624+ self.name = ''
1625+ self.continent = ''
1626+ self.proj = epsg2proj(4326)
1627+
1628+ self.setdefaultparameters()
1629+
1630+ if len(args):
1631+ options = pairoptions(*args)
1632+
1633+ #recover field values
1634+ self.boundaries = options.getfieldvalue('boundaries', [])
1635+ self.name = options.getfieldvalue('name', '')
1636+ self.continent = options.getfieldvalue('continent', '')
1637+
1638+ # figure out projection string
1639+ if options.exist('epsg'):
1640+ if options.exist('proj'):
1641+ raise RuntimeError('Error in constructor for basin %s. Cannot supply epsg and proj at the same time!'.format(self.name))
1642+ epsg = options.getfieldvalue('epsg', '')
1643+ proj = epsg2proj(epsg)#convert to PROJ.4 format
1644+ elif options.exist('proj'):
1645+ if options.exist('epsg'):
1646+ raise RuntimeError('Error in constructor for basin {}. Cannot supply proj and epsg at the same time!'.format(self.name))
1647+ proj = options.getfieldvalue('proj', '')
1648+ else:
1649+ proj = '+proj=longlat +datum=WGS84 +no_defs'
1650+
1651+ self.proj = proj
1652+ #}}}
1653+
1654+ def __repr__(self): # {{{
1655+ s = ' basin parameters:\n'
1656+ s += '{}\n'.format(fielddisplay(self, 'continent', 'continent name'))
1657+ s += '{}\n'.format(fielddisplay(self, 'name', 'basin name'))
1658+ s += '{}\n'.format(fielddisplay(self, 'proj', 'basin projection string (follows PROJ.4 standard'))
1659+ s += '{}\n'.format(fielddisplay(self, 'boundaries','list of boundary objects'))
1660+ for i in range(len(self.boundaries)):
1661+ s += ' boundary #{}: {}\n'.format((i + 1), self.boundaries[i])
1662+
1663+ return s
1664+ #}}}
1665+
1666+ def setdefaultparameters(self): # {{{
1667+ self.name = ''
1668+ self.continent = ''
1669+ self.proj = '+proj=longlat +datum=WGS84 +no_defs' #EPSG 4326
1670+ self.boundaries = []
1671+
1672+ return self
1673+ #}}}
1674+
1675+ def isnameany(self, *args): #{{{
1676+ for arg in args:
1677+ if arg == self.name:
1678+ return 1
1679+ return 0
1680+ #}}}
1681+
1682+ def iscontinentany(self, *args): #{{{
1683+ for arg in args:
1684+ if arg == self.continent:
1685+ return 1
1686+ return 0
1687+ #}}}
1688+
1689+ def outputname(self, *args): #{{{
1690+ #recover options
1691+ options = pairoptions(*args)
1692+ extension = options.getfieldvalue('extension', 1)
1693+
1694+ path, name, ext = fileparts(*args)
1695+ if extension:
1696+ output = '{}{}'.format(name, ext)
1697+ else:
1698+ output = name
1699+
1700+ return output
1701+ #}}}
1702+
1703+ def plot(self, *args): #{{{
1704+ #add option
1705+ for i in range(len(self.boundaries)):
1706+ self.boundaries[i].plot('proj', self.proj, *args)
1707+ #}}}
1708+
1709+ def plot3d(self, *args): #{{{
1710+ #add option
1711+ for i in range(len(self.boundaries)):
1712+ self.boundaries[i].plot3d(*args)
1713+ #}}}
1714+
1715+ def contour(self, *args): #{{{
1716+ #recover options
1717+ options = pairoptions(*args)
1718+ x = []
1719+ y = []
1720+
1721+ #go through boundaries, recover edges and project them in the basin epsg reference frame
1722+ for i in range(len(self.boundaries)):
1723+ boundary = self.boundaries[i]
1724+ contour = boundary.edges()
1725+ contour.x, contour.y = gdaltransform(contour.x, contour.y, boundary.proj, self.proj)
1726+ x = [[x], [contour.x]]
1727+ y = [[y], [contour.y]]
1728+
1729+ #close the contour
1730+ if x[-1] != x[0] or y[-1] != y[0]:
1731+ x.append(x[0])
1732+ y.append(y[0])
1733+
1734+ setattr(out, 'x', x)
1735+ setattr(out, 'y', y)
1736+ setattr(out, 'nods', len(x))
1737+
1738+ return out
1739+ #}}}
1740+
1741+ def checkconsistency(self, *args): #{{{
1742+ #recover options
1743+ options = pairoptions(*args)
1744+
1745+ #figure out if two boundaries are identical
1746+ for i in range(len(self.boundaries)):
1747+ namei = self.boundaries[i].shpfilename
1748+ for j in range(1, len(self.boundaries)):
1749+ namej = self.boundaries[j].shpfilename
1750+ if namei == namej:
1751+ raise RuntimeError('Basin {} has two identical boundaries named {}'.format(self.name, namei))
1752+
1753+ #go through boundaries and check their consistency
1754+ for i in range(len(self.boundaries)):
1755+ boundary == self.boundaries[i]
1756+ boundary.checkconsistency()
1757+ #}}}
1758+
1759+ def contourplot(self, *args): #{{{
1760+ contour = self.contour()
1761+ plot(contour.x, contour.y, 'r*-')
1762+ #}}}
1763+
1764+ def shapefilecrop(self, *args): #{{{
1765+ #recover options
1766+ options = pairoptions(*args)
1767+ threshold = options.getfieldvalue('threshold', .65) #.65 degrees lat, long
1768+ inshapefile = options.getfieldvalue('shapefile')
1769+ outshapefile = options.getfieldvalue('outshapefile', '')
1770+
1771+ if options.exist('epsgshapefile') and options.exist('projshapefile'):
1772+ raise RuntimeError('Basin shapefilecrop error messgae: cannot specify epsgshapefile and projshapefile at the same time')
1773+
1774+ if options.exist('epsgshapefile'):
1775+ projshapefile = epsg2proj(options.getfieldvalue('epsgshapefile'))
1776+ elif options.exist('projshapefile'):
1777+ projshapefile = options.getfieldvalue('projshapefile')
1778+ else:
1779+ raise RuntimeError('Basin shapefilecrop error message: epsgshapefile or projshapefile should be specified')
1780+
1781+ #create list of contours that have critical length > threshold (in lat, long)
1782+ contours = shpread(inshapefile)
1783+ llist = []
1784+ for i in range(len(contours)):
1785+ contour = contours[i]
1786+ carea = polyarea(contour.x, contour.y)
1787+ clength = math.sqrt(carea)
1788+ if clength < threshold:
1789+ llist = np.concatenate(llist, i)
1790+ setattr(contours, llist, [])
1791+
1792+ #project onto reference frame
1793+ for i in range(len(contours)):
1794+ h = contours[i]
1795+ h.x, h.y = gdaltransform(h.x, h.y, projshapefile, self.proj)
1796+ contours[i].x = h.x
1797+ contours[i].y = h.y
1798+
1799+ #only keep the contours that are inside the basin (take centroids)
1800+ ba = self.contour()
1801+ flags = np.zeros(len(contours))
1802+ for i in range(len(contours))
1803+ h = contours[i]
1804+ isin = inpolygon(h.x, h.y, ba.x, ba.y)
1805+ if len(np.where(isin == 0, isin, isin)):
1806+ flags[i] = 1
1807+ pos = flags.nonzero()
1808+ contours[pos] = []
1809+
1810+ #Two options
1811+ if outputshapefile == '':
1812+ output = contours
1813+ else:
1814+ shpwrite(contours, outputshapefile)
1815+ #}}}
1816+#}}}
1817Index: ../trunk-jpl/src/m/classes/boundary.m
1818===================================================================
1819--- ../trunk-jpl/src/m/classes/boundary.m (revision 25064)
1820+++ ../trunk-jpl/src/m/classes/boundary.m (revision 25065)
1821@@ -19,35 +19,32 @@
1822 end
1823 methods
1824 function self = boundary(varargin) % {{{
1825- switch nargin
1826- case 0
1827- self=setdefaultparameters(self);
1828- otherwise
1829- self=setdefaultparameters(self);
1830- options=pairoptions(varargin{:});
1831-
1832- %recover field values:
1833- self.shppath=getfieldvalue(options,'shppath','');
1834- self.shpfilename=getfieldvalue(options,'shpfilename','');
1835- self.orientation=getfieldvalue(options,'orientation','normal');
1836+ self=setdefaultparameters(self);
1837
1838- %figure out projection string:
1839+ if nargin > 0,
1840+ options=pairoptions(varargin{:});
1841+
1842+ %recover field values:
1843+ self.shppath=getfieldvalue(options,'shppath','');
1844+ self.shpfilename=getfieldvalue(options,'shpfilename','');
1845+ self.orientation=getfieldvalue(options,'orientation','normal');
1846+
1847+ %figure out projection string:
1848+ if exist(options,'epsg'),
1849+ if exist(options,'proj'),
1850+ error(['Error in constructor for boundary ' self.shppath '. Cannot supply epsg and proj at the same time!']);
1851+ end
1852+ epsg=getfieldvalue(options,'epsg');
1853+ proj=epsg2proj(epsg); % convert to PROJ.4 format
1854+ elseif exist(options,'proj'),
1855 if exist(options,'epsg'),
1856- if exist(options,'proj'),
1857- error(['Error in constructor for boundary ' self.shppath '.Cannot supply epsg and proj at the same time!']);
1858- end
1859- epsg=getfieldvalue(options,'epsg');
1860- %convert into proj4:
1861- proj=epsg2proj(epsg);
1862- elseif exist(options,'proj'),
1863- if exist(options,'epsg'),
1864- error(['Error in constructor for boundary ' self.shppath '.Cannot supply epsg and proj at the same time!']);
1865- end
1866- proj=getfieldvalue(options,'proj');
1867- else
1868- proj= '+proj=longlat +datum=WGS84 +no_defs';
1869+ error(['Error in constructor for boundary ' self.shppath '. Cannot supply proj and epsg at the same time!']);
1870 end
1871- self.proj=proj;
1872+ proj=getfieldvalue(options,'proj');
1873+ else
1874+ proj= '+proj=longlat +datum=WGS84 +no_defs';
1875+ end
1876+ self.proj=proj;
1877 end
1878 end % }}}
1879 function self = setdefaultparameters(self) % {{{
1880@@ -54,7 +51,7 @@
1881 self.shppath='';
1882 self.shpfilename='';
1883 self.orientation='normal';
1884- self.proj='+proj=longlat +datum=WGS84 +no_defs '; %EPSG 4326
1885+ self.proj='+proj=longlat +datum=WGS84 +no_defs'; %EPSG 4326
1886 end % }}}
1887 function disp(self) % {{{
1888 disp(sprintf(' boundary parameters:'));
1889@@ -69,19 +66,19 @@
1890 output=self.shpfilename;
1891 end % }}}
1892 function output=edges(self) % {{{
1893-
1894- %read domain:
1895- [path,name,ext]=fileparts(self.shpfilename);
1896- if ~strcmpi(ext,'shp'),
1897- ext='shp';
1898- end
1899- output=shpread([self.shppath '/' name '.' ext]);
1900
1901- %do we reverse?
1902- if strcmpi(self.orientation,'reverse'),
1903- output.x=flipud(output.x);
1904- output.y=flipud(output.y);
1905- end
1906+ %read domain:
1907+ [path,name,ext]=fileparts(self.shpfilename);
1908+ if ~strcmpi(ext,'shp'),
1909+ ext='shp';
1910+ end
1911+ output=shpread([self.shppath '/' name '.' ext]);
1912+
1913+ %do we reverse?
1914+ if strcmpi(self.orientation,'reverse'),
1915+ output.x=flipud(output.x);
1916+ output.y=flipud(output.y);
1917+ end
1918 end % }}}
1919 function plot(self,varargin) % {{{
1920 %recover options
1921@@ -100,10 +97,9 @@
1922 fontsize=getfieldvalue(options,'fontsize',10);
1923 label=getfieldvalue(options,'label','on');
1924
1925- if (exist(options,'epsg') & exist(options,'proj')),
1926- error('Boundary plot error message: cannot specify epsg and proj at the same time for plot projection');
1927- end
1928- if exist(options,'epsg'),
1929+ if (exist(options,'epsg'),
1930+ if exist(options,'proj')),
1931+ error('Boundary plot error message: cannot specify epsg and proj at the same time for plot projection');
1932 proj=epsg2proj(getfieldvalue(options,'epsg'));
1933 elseif exist(options,'proj'),
1934 proj=getfieldvalue(options,'proj');
1935@@ -118,7 +114,7 @@
1936 end
1937 domain=shpread([self.shppath '/' name '.' ext]);
1938
1939- %convert boundary to another reference frame: {{{
1940+ %convert boundary to another reference frame: {{{
1941 for i=1:length(domain),
1942 try,
1943 [x,y] = gdaltransform(domain(i).x,domain(i).y,self.proj,proj);
1944@@ -168,7 +164,7 @@
1945 dmax=max(distance);
1946 isidentical=find(distance<dmax*tolerance);
1947 if ~isempty(isidentical),
1948- error(['boundary ' shpfilename ' has two vertices extremely close to one another']);
1949+ error(['boundary ' shpfilename ' has two vertices extremely close to one another']);
1950 end
1951 end
1952 end % }}}
1953@@ -189,6 +185,16 @@
1954 fontsize=getfieldvalue(options,'fontsize',10);
1955 label=getfieldvalue(options,'label','on');
1956
1957+ if (exist(options,'epsg'),
1958+ if exist(options,'proj')),
1959+ error('Boundary plot error message: cannot specify epsg and proj at the same time for plot projection');
1960+ proj=epsg2proj(getfieldvalue(options,'epsg'));
1961+ elseif exist(options,'proj'),
1962+ proj=getfieldvalue(options,'proj');
1963+ else
1964+ proj=epsg2proj(4326);
1965+ end
1966+
1967 %read domain:
1968 [path,name,ext]=fileparts(self.shpfilename);
1969 if ~strcmpi(ext,'shp'),
1970Index: ../trunk-jpl/src/m/plot/plotmodel.py
1971===================================================================
1972--- ../trunk-jpl/src/m/plot/plotmodel.py (revision 25064)
1973+++ ../trunk-jpl/src/m/plot/plotmodel.py (revision 25065)
1974@@ -1,6 +1,3 @@
1975-import numpy as np
1976-from plotoptions import plotoptions
1977-from plot_manager import plot_manager
1978 from math import ceil, sqrt
1979
1980 try:
1981@@ -8,23 +5,37 @@
1982 from mpl_toolkits.axes_grid1 import ImageGrid
1983 except ImportError:
1984 print("could not import pylab, matplotlib has not been installed, no plotting capabilities enabled")
1985+import numpy as np
1986
1987+from plot_manager import plot_manager
1988+from plotoptions import plotoptions
1989
1990+
1991 def plotmodel(md, *args):
1992- ''' at command prompt, type 'plotdoc()' for additional documentation
1993 '''
1994+ PLOTMODEL - At command prompt, type 'plotdoc()' for additional
1995+ documentation.
1996
1997+ TODO:
1998+ - Fix 'plotdoc()', as it is not currently working.
1999+ '''
2000+
2001 #First process options
2002 options = plotoptions(*args)
2003- #get number of subplots
2004- subplotwidth = ceil(sqrt(options.numberofplots))
2005+
2006 #Get figure number and number of plots
2007 figurenumber = options.figurenumber
2008 numberofplots = options.numberofplots
2009
2010- #get hold
2011- hold = options.list[0].getfieldvalue('hold', False)
2012+ #get number of subplots
2013+ subplotwidth = ceil(sqrt(numberofplots))
2014
2015+ # TODO: Check that commenting this out is correct; we do not need a hold
2016+ # under matplotlib, right?
2017+ #
2018+ # #get hold
2019+ # hold = options.list[0].getfieldvalue('hold', False)
2020+
2021 #if nrows and ncols specified, then bypass
2022 if options.list[0].exist('nrows'):
2023 nrows = options.list[0].getfieldvalue('nrows')
2024@@ -43,14 +54,18 @@
2025 nrows = int(nrows)
2026
2027 #check that nrows and ncols were given at the same time!
2028- if not nr == nc:
2029- raise Exception('error: nrows and ncols need to be specified together, or not at all')
2030+ if nr != nc:
2031+ raise Exception('plotmodel error message: nrows and ncols need to be specified together, or not at all')
2032
2033- #Go through plots
2034+ # Go through plots
2035+ #
2036+ # NOTE: The following is where Python + matplolib differs substantially in
2037+ # implementation and inteface from MATLAB.
2038+ #
2039+ # Sources:
2040+ # - https://matplotlib.org/api/_as_gen/mpl_toolkits.axes_grid1.axes_grid.ImageGrid.html
2041+ #
2042 if numberofplots:
2043- #if plt.fignum_exists(figurenumber):
2044- # plt.cla()
2045-
2046 #if figsize specified
2047 if options.list[0].exist('figsize'):
2048 figsize = options.list[0].getfieldvalue('figsize')
2049@@ -62,36 +77,64 @@
2050 backgroundcolor = options.list[0].getfieldvalue('backgroundcolor', (0.7, 0.7, 0.7))
2051 fig.set_facecolor(backgroundcolor)
2052
2053- translator = {'on': 'each',
2054- 'off': 'None',
2055- 'one': 'single'}
2056- # options needed to define plot grid
2057+ # options needed to define plot grid
2058 plotnum = options.numberofplots
2059 if plotnum == 1:
2060 plotnum = None
2061- direction = options.list[0].getfieldvalue('direction', 'row') # row, column
2062- axes_pad = options.list[0].getfieldvalue('axes_pad', 0.25)
2063- add_all = options.list[0].getfieldvalue('add_all', True) # True, False
2064- share_all = options.list[0].getfieldvalue('share_all', True) # True, False
2065- label_mode = options.list[0].getfieldvalue('label_mode', 'L') # 1, L, all
2066+
2067+ # NOTE: The inline comments for each of the following parameters are
2068+ # taken from https://matplotlib.org/api/_as_gen/mpl_toolkits.axes_grid1.axes_grid.ImageGrid.html
2069+ #
2070+ direction = options.list[0].getfieldvalue('direction', 'row') # {"row", "column"}, default: "row"
2071+ axes_pad = options.list[0].getfieldvalue('axes_pad', 0.25) # float or (float, float), default : 0.02; Padding or (horizonal padding, vertical padding) between axes, in inches
2072+ add_all = options.list[0].getfieldvalue('add_all', True) # bool, default: True
2073+ share_all = options.list[0].getfieldvalue('share_all', True) # bool, default: False
2074+ label_mode = options.list[0].getfieldvalue('label_mode', 'L') # {"L", "1", "all"}, default: "L"; Determines which axes will get tick labels: "L": All axes on the left column get vertical tick labels; all axes on the bottom row get horizontal tick labels;. "1": Only the bottom left axes is labelled. "all": all axes are labelled.
2075+
2076+ # Translate MATLAB colorbar mode to matplotlib
2077+ #
2078+ # TODO:
2079+ # - Add 'edge' option (research if there is a corresponding option in
2080+ # MATLAB)?
2081+ #
2082 colorbar = options.list[0].getfieldvalue('colorbar', 'on') # on, off (single)
2083- cbar_mode = translator[colorbar]
2084- cbar_location = options.list[0].getfieldvalue('colorbarpos', 'right') # right, top
2085- cbar_size = options.list[0].getfieldvalue('colorbarsize', '5%')
2086- cbar_pad = options.list[0].getfieldvalue('colorbarpad', 0.025) # None or %
2087
2088- axgrid = ImageGrid(fig, 111,
2089- nrows_ncols=(nrows, ncols),
2090- direction=direction,
2091- axes_pad=axes_pad,
2092- add_all=add_all,
2093- share_all=share_all,
2094- label_mode=label_mode,
2095- cbar_mode=cbar_mode,
2096- cbar_location=cbar_location,
2097- cbar_size=cbar_size,
2098- cbar_pad=cbar_pad)
2099+ if colorbar == 'on':
2100+ colorbar = 'each'
2101+ elif colorbar == 'one':
2102+ colorbar = 'single'
2103+ elif colorbar == 'off':
2104+ colorbar = 'None'
2105+ else:
2106+ raise RuntimeError('plotmodel error: colorbar mode \'{}\' is not a valid option'.format(colorbar))
2107
2108+ cbar_mode = colorbar # {"each", "single", "edge", None }, default: None
2109+ cbar_location = options.list[0].getfieldvalue('colorbarpos', 'right') # {"left", "right", "bottom", "top"}, default: "right"
2110+ cbar_pad = options.list[0].getfieldvalue('colorbarpad', 0.025) # float, default: None
2111+ cbar_size = options.list[0].getfieldvalue('colorbarsize', '5%') # size specification (see Size.from_any), default: "5%"
2112+
2113+ # NOTE: Second parameter is:
2114+ #
2115+ # rect(float, float, float, float) or int
2116+ #
2117+ # The axes position, as a (left, bottom, width, height) tuple or as a
2118+ # three-digit subplot position code (e.g., "121").
2119+ #
2120+ axgrid = ImageGrid(
2121+ fig,
2122+ 111,
2123+ nrows_ncols=(nrows, ncols),
2124+ direction=direction,
2125+ axes_pad=axes_pad,
2126+ add_all=add_all,
2127+ share_all=share_all,
2128+ label_mode=label_mode,
2129+ cbar_mode=cbar_mode,
2130+ cbar_location=cbar_location,
2131+ cbar_size=cbar_size,
2132+ cbar_pad=cbar_pad
2133+ )
2134+
2135 if cbar_mode == 'None':
2136 for ax in axgrid.cbar_axes:
2137 fig._axstack.remove(ax)
2138Index: ../trunk-jpl/src/m/plot/plot_unit.py
2139===================================================================
2140--- ../trunk-jpl/src/m/plot/plot_unit.py (revision 25064)
2141+++ ../trunk-jpl/src/m/plot/plot_unit.py (revision 25065)
2142@@ -1,6 +1,4 @@
2143 from cmaptools import getcolormap, truncate_colormap
2144-from plot_quiver import plot_quiver
2145-import numpy as np
2146 try:
2147 import matplotlib as mpl
2148 import matplotlib.pyplot as plt
2149@@ -9,17 +7,20 @@
2150 from mpl_toolkits.mplot3d.art3d import Poly3DCollection
2151 except ImportError:
2152 print("could not import pylab, matplotlib has not been installed, no plotting capabilities enabled")
2153+import numpy as np
2154
2155+from plot_quiver import plot_quiver
2156
2157+
2158 def plot_unit(x, y, z, elements, data, is2d, isplanet, datatype, options, fig, axgrid, gridindex):
2159- """
2160+ '''
2161 PLOT_UNIT - unit plot, display data
2162
2163 Usage:
2164- plot_unit(x, y, z, elements, data, is2d, isplanet, datatype, options)
2165+ plot_unit(x, y, z, elements, data, is2d, isplanet, datatype, options)
2166
2167 See also: PLOTMODEL, PLOT_MANAGER
2168- """
2169+ '''
2170 #if we are plotting 3d replace the current axis
2171 if not is2d:
2172 axgrid[gridindex].axis('off')
2173@@ -227,21 +228,16 @@
2174 else:
2175 raise ValueError('plot_unit error: 3D node plot not supported yet')
2176 return
2177-
2178 # }}}
2179 # {{{ plotting P1 Patch (TODO)
2180-
2181 elif datatype == 4:
2182 print('plot_unit message: P1 patch plot not implemented yet')
2183 return
2184-
2185 # }}}
2186 # {{{ plotting P0 Patch (TODO)
2187-
2188 elif datatype == 5:
2189 print('plot_unit message: P0 patch plot not implemented yet')
2190 return
2191-
2192 # }}}
2193 else:
2194 raise ValueError('datatype = %d not supported' % datatype)
2195Index: ../trunk-jpl/src/m/plot/applyoptions.py
2196===================================================================
2197--- ../trunk-jpl/src/m/plot/applyoptions.py (revision 25064)
2198+++ ../trunk-jpl/src/m/plot/applyoptions.py (revision 25065)
2199@@ -1,17 +1,17 @@
2200-import numpy as np
2201 from cmaptools import getcolormap
2202-from plot_contour import plot_contour
2203-from plot_streamlines import plot_streamlines
2204-from expdisp import expdisp
2205-
2206 try:
2207- from matplotlib.ticker import MaxNLocator
2208 import matplotlib as mpl
2209 import matplotlib.pyplot as plt
2210+ from matplotlib.ticker import MaxNLocator
2211 except ImportError:
2212 print("could not import pylab, matplotlib has not been installed, no plotting capabilities enabled")
2213+import numpy as np
2214
2215+from expdisp import expdisp
2216+from plot_contour import plot_contour
2217+from plot_streamlines import plot_streamlines
2218
2219+
2220 def applyoptions(md, data, options, fig, axgrid, gridindex):
2221 '''
2222 APPLYOPTIONS - apply options to current plot
2223@@ -19,10 +19,10 @@
2224 'plotobj' is the object returned by the specific plot call used to
2225 render the data. This object is used for adding a colorbar.
2226
2227- Usage:
2228- applyoptions(md, data, options)
2229+ Usage:
2230+ applyoptions(md, data, options)
2231
2232- See also: PLOTMODEL, PARSE_OPTIONS
2233+ See also: PLOTMODEL, PARSE_OPTIONS
2234 '''
2235
2236 # get handle to current figure and axes instance
2237@@ -33,9 +33,11 @@
2238 fontsize = options.getfieldvalue('fontsize', 8)
2239 fontweight = options.getfieldvalue('fontweight', 'normal')
2240 fontfamily = options.getfieldvalue('fontfamily', 'sans - serif')
2241- font = {'fontsize': fontsize,
2242- 'fontweight': fontweight,
2243- 'family': fontfamily}
2244+ font = {
2245+ 'fontsize': fontsize,
2246+ 'fontweight': fontweight,
2247+ 'family': fontfamily
2248+ }
2249 # }}}
2250 # {{{ title
2251 if options.exist('title'):
Note: See TracBrowser for help on using the repository browser.