-
Notifications
You must be signed in to change notification settings - Fork 9
/
quivermc.m
1408 lines (1192 loc) · 44.6 KB
/
quivermc.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
function [hvectors,cb] = quivermc(lat,lon,u,v,varargin)
% QUIVERMC is an adapted version of Andrew Roberts' ncquiverref.
% Dr. Roberts' function and this function fix a couple of problems with Matlab's quiverm function.
% The two primary issues with quiverm are as follows:
%
% 1. Matlab's quiverm confoundingly mixes up u and v. By convention in the
% Earth sciences, u is the zonal component where eastward is positive and
% v is the meridional component where northward is positive. Matlab gets this wrong, but the
% quivermc function described here gets it right.
%
% 2. For reasons related to ship travel and some old legacy code from Navy
% guys decades ago, Matlab's quiverm scales vectors in a strange way that
% depends on latitude. If you're plotting some absolute field like wind
% vectors, there is no physical reason that you would want to scale vectors
% in such a way that their zonal components shrink to zero at the poles.
%
% In addition to fixing the problems described above, quivermc also
% allows a few extra options including color settings, arrow density, and
% options for converging or diverging flow.
%
%% Syntax
%
% quivermc(lat,lon,u,v)
% quivermc(...,'units',unitString)
% quivermc(...,'color',arrowcolor)
% quivermc(...,'colormap',colorMap)
% quivermc(...,'colorbar','ColorbarLocation')
% quivermc(...,'density',densityVal)
% quivermc(...,'arrowstyle',arrowStyle)
% quivermc(...,'linewidth',lineWidth)
% quivermc(...,'reference',referenceScale)
% h = quivermc(...)
% [h,cb] = quivermc(...)
%
%% Description
%
% quivermc(lat,lon,u,v) plots vectors of zonal and meridional components
% u and v at locations given by lat and lon.
%
% quivermc(...,'units',unitString) prints any user-specified units
% alongside a reference vector.
%
% quivermc(...,'color',arrowcolor) sets all arrows to the color given by
% arrowcolor, which can be a string (e.g. 'blue', or 'r') or RGB value.
% Default color is black.
%
% quivermc(...,'colormap',colorMap) colors vectors scaled relative to
% their magnitude using any colormap such as jet or autumn(256).
%
% |quivermc(...,'colorbar','ColorbarLocation')| places a colorbar at a
% specified location. The argument |'colorbar','on'| may be used to place a
% colorbar to the outside right of the plot, or a location may be set as
%
% * 'EastOutside', 'vertical', or 'on' Outside right
% * 'SouthOutside' or 'horizontal' Outside bottom
% * 'North Inside' plot box near top
% * 'South Inside' bottom
% * 'East Inside' right
% * 'West Inside' left
% * 'NorthOutside' Outside plot box near top
% * 'SouthOutside' Outside bottom
% * 'WestOutside' Outside left
%
% quivermc(...,'density',densityVal) allows user-declared downsampling of
% input data. By default, if the input grid is larger than about 50x50,
% quivermc will attempt to downsample your data to some dimensions close
% to 50x50. This is because large datasets take time to plot, and the
% arrows become so small they're hard to see. If your dataset is 400x400
% and you would like to plot about 100 arrows by 100 arrows instead of the
% default 50x50, use the name-value pair 'density',25 to specify 25
% percent of the data are to be plotted in each dimension.
%
% quivermc(...,'arrowstyle',arrowStyle) specifies whether the arrow's
% tail or tip is located at its respective data point. By default, arrows
% are centered about their data points. To pin arrow tails at their data
% points, declare 'arrowstyle','tail' or 'arrowstyle','divergent'. To
% pin arrow heads at their data points choose 'arrowstyle','tip' or
% 'arrowstyle','convergent'.
%
% quivermc(...,'linewidth',lineWidth) sets the linewidth of plotted
% arrows in points.
%
% quivermc(...,'reference',referenceScale) declares the scale by which arrows are
% plotted. Can be 'median', which scales vectors relative to the median magnitude,
% 'max', which scales vectors relative to the maximum magnitude, or
% 'equal' to make all vectors equal in size. The referenceScale may
% also be a scalar value of your choosing. Default is 'max'.
%
% h = quivermc(...) returns the vector handles of plotted arrow objects.
%
% |[h,cb] = quivermc(...)| returns the handle |cb| of the colorbar.
%
%
%% Examples:
%
% % Make up some data:
% load wind u v
% u = squeeze(u(:,:,1));
% v = squeeze(v(:,:,1));
% u(1:30,:) = u(1:30,:)+5;
% u(31:end,:) = u(31:end,:)-3;
% lat = repmat((20:-1:-14)',1,41);
% lon = repmat(-160:-120,35,1);
%
% % Initialize a map:
% worldmap([min(lat(:))-1 max(lat(:))+1],[min(lon(:))-1 max(lon(:))+1]);cla;
%
% % Use the quivermc function:
% quivermc(lat,lon,u,v)
% quivermc(lat,lon,u,v,'units','miles per hour') % specifies units
% quivermc(lat,lon,u,v,'color','r') % sets all arrows to 'red'
% quivermc(lat,lon,u,v,'colormap',hot(256),'units','m/s') % colors arrows with a hot colormap
% quivermc(lat,lon,u,v,'density',33.3) % downsamples data by a factor of 3
% quivermc(lat,lon,u,v,'density',33.3,'arrowstyle','divergent','color',[.4 .6 .2]) % pins arrows at their tails
% quivermc(lat,lon,u,v,'density',33.3,'arrowstyle','divergent','color',[.4 .6 .2],'reftype','median') % sizes arrows relative to median magnitude
%
%
%% Citing Antarctic Mapping Tools
% This function was developed for Antarctic Mapping Tools for Matlab (AMT). If AMT is useful for you,
% please cite our paper:
%
% Greene, C. A., Gwyther, D. E., & Blankenship, D. D. Antarctic Mapping Tools for Matlab.
% Computers & Geosciences. 104 (2017) pp.151-157.
% http://dx.doi.org/10.1016/j.cageo.2016.08.003
%
% @article{amt,
% title={{Antarctic Mapping Tools for \textsc{Matlab}}},
% author={Greene, Chad A and Gwyther, David E and Blankenship, Donald D},
% journal={Computers \& Geosciences},
% year={2017},
% volume={104},
% pages={151--157},
% publisher={Elsevier},
% doi={10.1016/j.cageo.2016.08.003},
% url={http://www.sciencedirect.com/science/article/pii/S0098300416302163}
% }
%
%% Author Info
% Original function ncquiverref written by Andrew Roberts
% Naval Postgraduate School
% Tested using MATLAB Version 7.11.0.584 (R2010b)
%
% $Id: ncquiverref.m 254 2011-02-09 05:26:08Z aroberts $
%
% Adapted into quivermc by Chad A. Greene
% Institute for Geophysics
% The University of Texas at Austin
% July 2014
% Tested on Matlab 2012b with Mac OSX 10.8.5
%
% Updated August 2014 to include John Barber's calcticks function
% Calcticks can be found here: http://www.mathworks.com/matlabcentral/fileexchange/30671
%
% See also quiver, quiverm, and quiver3m.
%% Input checking:
% make sure the mapping toolbox is present
h=ver('map') ; if isempty(h) ; error('Mapping toolbox not installed') ; end
% error checking of inputs
assert(nargin>=4,'Not enough inputs.');
assert(numel(lat)==numel(lon)&numel(lat)==numel(u)&numel(lat)==numel(v),'Dimensions of lat, lon, u, and v must agree.')
assert(isscalar(lat)==0,'Input lat, lon, u, and v must be a grid.')
assert(isvector(lat)==0,'Input lat, lon, u, and v must be a grid.')
%% Set defaults and change them depending on user preferences:
refvec = false;
colorbarOn = false;
units = '';
tmp = strncmpi(varargin,'unit',4);
if any(tmp)
units = varargin{find(tmp)+1};
tmp(find(tmp)+1)=1;
varargin = varargin(~tmp);
refvec = true;
end
reftype = 'max';
tmp = strncmpi(varargin,'ref',3)|strncmpi(varargin,'scale',5);
if any(tmp)
reftype = varargin{find(tmp)+1};
tmp(find(tmp)+1)=1;
varargin = varargin(~tmp);
end
tmp = strcmpi(varargin,'colormap');
if any(tmp)
cmap = varargin{find(tmp)+1};
tmp(find(tmp)+1)=1;
varargin = varargin(~tmp);
end
tmp = strcmpi(varargin,'colorbar');
if any(tmp)
colorbarOn = true;
cbLocation = varargin{find(tmp)+1};
cbLocation = strrep(cbLocation,'vertical','WestOutside');
cbLocation = strrep(cbLocation,'on','EastOutside');
cbLocation = strrep(cbLocation,'horizontal','SouthOutside');
tmp(find(tmp)+1)=1;
varargin = varargin(~tmp);
end
if nargout>1 && colorbarOn==false
error('Too many output arguments.')
end
veccol='k';
tmp = strcmpi(varargin,'color');
if any(tmp)
veccol = varargin{find(tmp)+1};
tmp(find(tmp)+1)=1;
varargin = varargin(~tmp);
end
arrowStyle = 'centered';
tmp = strcmpi(varargin,'arrowstyle');
if any(tmp)
tmp2 = strncmpi(varargin,'div',3)|strcmpi(varargin,'tail');
if any(tmp2)
arrowStyle = 'divergent';
end
tmp2 = strncmpi(varargin,'con',3)|strcmpi(varargin,'tip')|strcmpi(varargin,'head');
if any(tmp2)
arrowStyle = 'convergent';
end
tmp(find(tmp)+1)=1;
varargin = varargin(~tmp);
end
changelinewidth = false;
tmp = strncmpi(varargin,'linewi',6)|strncmpi(varargin,'wid',3)|...
strcmpi(varargin,'arrowwidth')|strcmpi(varargin,'arrow width');
if any(tmp)
lineWidth = varargin{find(tmp)+1};
tmp(find(tmp)+1)=1;
varargin = varargin(~tmp);
assert(isnumeric(lineWidth)==1,'Line width must be a scalar.')
changelinewidth = true;
end
skipstep = 1; % If the data set is small, do not skip any vectors
defaultWidth = 50; % but if the data set is large, plot about 50x50 arrows for a large square dataset
if sqrt(numel(lat))/defaultWidth>1
skipstep = round(sqrt(numel(lat))/defaultWidth);
end
tmp = strncmpi(varargin,'dens',4)|strncmpi(varargin,'arrowdens',8)|strncmpi(varargin,'downsa',6);
if any(tmp)
declaredDensityVal = varargin{find(tmp)+1};
assert(isnumeric(declaredDensityVal)==1,'Arrow density value must be numeric.')
assert(declaredDensityVal>0&&declaredDensityVal<=100,'Arrow density must be greater than 0 and no more than 100.')
skipstep = round(100/declaredDensityVal);
end
lat = lat(1:skipstep:end,1:skipstep:end);
lon = lon(1:skipstep:end,1:skipstep:end);
u = u(1:skipstep:end,1:skipstep:end);
v = v(1:skipstep:end,1:skipstep:end);
% get current axis
%h=get(gcf,'CurrentAxes');
h = gca;
assert(ismap(h)==1,'Current axes must be map axes.')
% If plotting on a matlab map, determine if the axes are map or cartesian
% coordinates, and if the former calculate mapping to plot axis, and
% then do vector field otherwise just plot the vector field.
%% Begin heavy lifting:
% get x and y location on the map
sz=size(lat);
mstruct=gcm;
[x,y] = mfwdtran(mstruct,lat,lon,h,'none');
xz=size(x);
if sz~=xz
error('Change in size of x using mfwdtran. Try changing surface to none in the code')
end
% get angle on the map, but do not distort the length according to the projection
% so that all vectors can use the same reference vector. DO NOT project
% the length of the vector to be different in x and y directions.
[th,z] = cart2pol(u,v);
[thproj,~] = vfwdtran(mstruct,lat,lon,90*ones(size(lat)));
[u,v] = pol2cart(th+deg2rad(thproj),z);
% remove masked grid points from the input by filling coordinates with NaN;
x(isnan(u))=NaN;
y(isnan(u))=NaN;
magnitude = hypot(u,v);
% Scale the vectors according to the reference arrow vector length based on
% the mean distance between grid points. This is a good measure, as it remains
% constant for multiple plots using the same grid with different values.
x1=abs(diff(x')); x2=abs(diff(x));
y1=abs(diff(y')); y2=abs(diff(y));
[~,z1] = cart2pol(x1,y1); [~,z2] = cart2pol(x2,y2);
scalelength=min(mean(z1(~isnan(z1))),mean(z2(~isnan(z2))));
% Calculate reference vector length based on rounded median
% or maximum value of plot. The default is median based.
if isnumeric(reftype)
refval=reftype;
elseif strncmpi(reftype,'median',3)
z(z==0)=NaN;
refval=median(z(~isnan(z)));
elseif strcmpi(reftype,'max')
refval=max(z(~isnan(z)));
elseif strcmpi(reftype,'equal')
magnitude = hypot(u,v);
z(z==0)=NaN;
refval=median(z(~isnan(z)));
u = u./magnitude;
v = v./magnitude;
else
end
% Remove NaN values that will not be plotted
% and turn points into a row of coordinates
u=u(~isnan(x))';
v=v(~isnan(x))';
y=y(~isnan(x))';
x=x(~isnan(x))';
% Set arrow size (1= full length of vector)
arrow=0.40;
% set scale value based on refval and scale length
roundp=floor(log10(refval));
refval=floor(refval/(10^roundp))*(10^roundp);
scale=scalelength/refval;
% adjust whether arrows are centered,
switch arrowStyle
case 'centered' % (centered is the default)
% Center vectors over grid points
xstart=x-0.5*scale*u;
xend=x+0.5*scale*u;
ystart=y-0.5*scale*v;
yend=y+0.5*scale*v;
case 'divergent'
% diverging arrows:
xstart=x;
xend=x+scale*u;
ystart=y;
yend=y+scale*v;
case 'convergent'
% arrows converge upon a point
xstart=x-scale*u;
xend=x;
ystart=y-scale*v;
yend=y;
end
% Get x coordinates of each vector plotted
lx = [xstart; x; ...
xstart+(1-arrow/3)*(xend-xstart); ...
xend-arrow*(scale*u+arrow*(scale*v)); ...
xend; ...
xend-arrow*(scale*u-arrow*(scale*v)); ...
xstart+(1-arrow/3)*(xend-xstart); ...
NaN(size(x))];
% Get y coordinates of each vector plotted
ly = [ystart; y; ...
ystart+(1-arrow/3)*(yend-ystart); ...
yend-arrow*(scale*v-arrow*(scale*u)); ...
yend; ...
yend-arrow*(scale*v+arrow*(scale*u)); ...
ystart+(1-arrow/3)*(yend-ystart); ...
NaN(size(y))];
% Plot the vectors
hvectors = line(lx,ly,'Color',veccol);
%% Color vectors with cmap if requested by user:
if exist('cmap','var')
magnitude = hypot(u,v);
minmag = min(magnitude(:))
maxmag = max(magnitude(:))
magind = 1+round((length(cmap(:,1))-1)*(magnitude(:)-minmag)./(maxmag - minmag));
if colorbarOn
colormap(gca,cmap);
cb = colorbar('location',cbLocation);
[newticks,newticklabels] = calcticks([minmag maxmag]);
newticks = (length(cmap(:,1))-1)*(newticks-minmag)/(maxmag-minmag);
switch lower(cbLocation)
case {'east','west','eastoutside','westoutside'}
set(cb,'ytick',newticks,'yticklabel',newticklabels);
ylabel(cb,units);
case {'north','south','northoutside','southoutside'}
set(cb,'xtick',newticks,'xticklabel',newticklabels);
xlabel(cb,units);
otherwise
error('Invalid colorbar location.')
end
end
for k = 1:length(magnitude(:))
set(hvectors(k),'Color',cmap(magind(k),:))
end
end
%% Draw the reference vector key at altitude 2 above the map and grid
if refvec
% Get the reference text string, formatted to powers of ten if required
reftext=[num2str(refval),' ',units,' '];
% Get the current axis limits
xlim=get(gca,'xlim'); xp1=xlim(1); xp2=xlim(2);
ylim=get(gca,'ylim'); yp1=ylim(1); yp2=ylim(2);
% set padding around the reference vector
padx=diff(xlim)/100;
pady=diff(ylim)/100;
% Set x position of reference vector
xend=xp2-padx;
xstart=xend-scalelength;
% Plot reference text in lower right hand corner
ht=text(xstart,yp1+pady,reftext,'Visible','off','Parent',gca,'FontSize',8.5,...
'VerticalAlignment','Bottom','HorizontalAlignment','Right');
textextent=get(ht,'Extent');
% Draw patch over area of vector key
xl=textextent(1)-padx;
xr=xp2;
yb=yp1;
yt=textextent(2)+textextent(4)+pady;
hp=patch([xl; xl; xr; xr],[yb; yt; yt; yb],[2; 2; 2; 2],'w',...
'LineWidth',0.5,'Parent',gca);
uistack(hp,'top');
% Redraw reference text on top of patch
ht=text(xstart,(yb+yt)/2,2.1,reftext,'Parent',gca,'FontSize',8.5,...
'VerticalAlignment','Middle','HorizontalAlignment','Right');
% Set y position of reference vector
yend=textextent(2)+textextent(4)/2;
ystart=yend;
% Get x coordinates of reference vector plotted
lx = [xstart; ...
xstart+(1-arrow/3)*(xend-xstart); ...
xend-arrow*scalelength; ...
xend; ...
xend-arrow*scalelength; ...
xstart+(1-arrow/3)*(xend-xstart); ...
NaN];
% Get y coordinates of reference vector plotted
ly = [ystart; ...
ystart+(1-arrow/3)*(yend-ystart); ...
yend+arrow*(arrow*scalelength); ...
yend; ...
yend-arrow*(arrow*scalelength); ...
ystart+(1-arrow/3)*(yend-ystart); ...
NaN];
% Get z coordinates of reference vector
lz = 2*ones(size(ly));
% Plot the reference vector
hrefvec = line(lx,ly,lz,'Color',veccol);
end
%% Change arrow width if requested by user:
if changelinewidth
set(hvectors,'linewidth',lineWidth)
if refvec
set(hrefvec,'linewidth',lineWidth)
end
end
%% Clean up:
if nargout==0
clear hvectors
end
end % function
%% CALCTICKS FUNCTION BY JOHN BARBER:
function [ticks,tickLabels,scaleStr,minorTicks,overhang] = ...
calcticks(limits,orientation,varargin)
% Calculate ticks and ticklabels for specified limits and text size
%
% SYNTAX
%
% TICKS = CALCTICKS
% TICKS = CALCTICKS(LIMITS)
% TICKS = CALCTICKS(LIMITS,ORIENTATION)
% TICKS = CALCTICKS(...,TEXTSIZE)
% TICKS = CALCTICKS(...,SCALE)
% TICKS = CALCTICKS(...,SEPARATEEXPONENT)
% TICKS = CALCTICKS(...,EXPONENTFONTSIZE)
% TICKS = CALCTICKS(...,MAXCHARS)
% TICKS = CALCTICKS(AXHANDLE,...)
% [TICKS,TICKLABELS,SCALESTR] = CALCTICKS(...)
% [...,MINORTICKS] = CALCTICKS(...)
% [...,OVERHANG] = CALCTICKS(...)
%
% DESCRIPTION
%
% TICKS = CALCTICKS Calculate ticks for the y-axis of the current axes,
% using the axes' limits and text properties.
%
% TICKS = CALCTICKS(LIMITS) Calculate ticks for the y-axis of the current
% axes, using the specified limits instead of the axes limits.
%
% TICKS = CALCTICKS(LIMITS,ORIENTATION) Calculate ticks for the x or y axis
% of the current axes, using the specified limits. ORIENTATION can be any
% of 'x','h','horizontal' to get ticks for the x-axis, and any of 'y','v',
% or 'vertical' for the y-axis.
%
% TICKS = CALCTICKS(...,TEXTSIZE) Calculate ticks using the specified text
% size. TEXTSIZE should be the size (height or width, depending on the
% selected orientation) of the string '2', in data units, using the desired
% font properties and axes size. TEXTSIZE is used to determine the maximum
% number of ticks that will fit in the specified data limits. See the
% REMARKS section for more information about determining the correct value
% for TEXTSIZE. If TEXTSIZE is not specified, CALCTICKS will calculate its
% value using the specified limits and text and position properties of the
% specified axes, or the current axes.
%
% TICKS = CALCTICKS(...,SCALE) Calculate ticks using the specified axis
% scaling. Valid inputs are 'linear' and 'log'. If SCALE is not
% specified, CALCTICKS will use the value of the 'XScale' or 'YScale'
% property of the specified axes or the current axes.
%
% TICKS = CALCTICKS(...,SEPARATEEXPONENT) If TRUE, calculate ticks and
% ticklabels, returning a separate string containing the data scale when
% the ticklabels use exponential notation. The scale string is of the form
% 'x 10^NN' where NN is the scale of the maximum absolute value of the
% limits, and the tick labels will be of the form '-1.2345', normalized to
% 10^NN.
%
% CALCTICKS determines automatically when to use exponential notation,
% and this setting will have no effect if the algorithm selects standard
% notation. If SEPARATEEXPONENT is FALSE and the determination is to use
% exponential notation, the ticklabels will be of the form '1.234e+011'.
%
% TICKS = CALCTICKS(...,EXPONENTFONTSIZE) If TRUE, include TEX markup in
% the ticklabel strings to set the font size of exponents to 7 (the
% default). If set to a number, use that value for the font size of
% exponents. If FALSE or 0, CALCTICKS will not include TEX markup to
% change the font size of exponents.
%
% TICKS = CALCTICKS(...,MAXCHARS) Set the maximum length (in characters) of
% ticklabels. This value determines the precision displayed in the
% ticklabels, and for horizontal (x) orientation, affects the tick spacing.
% The default maximum is 9 characters. Setting the value of MAXCHARS too
% low can result in invalid outputs. The actual label lengths are
% determined by the size of the tick interval relative to the data scale.
%
% [...,TICKLABELS] = CALCTICKS(...) Return a cell array of tick
% labels
%
% [...,SCALESTR] = CALCTICKS(...) In addition to ticks and ticklabels,
% return a separate string containing the scale for ticklabels displayed
% using exponential notation. If the ticklabels are not displayed using
% exponential notation (as determined internally by CALCTICKS), SCALESTR
% will be the empty string.
%
% [...,MINORTICKS] = CALCTICKS(...) For 'log' scale, return a vector of
% minor ticks spaced at the [2:9] points in each decade. If the major
% ticks are spaced at intervals greater than 3 decades, the minor ticks
% will be placed at the 'missing' decades. For 'linear' scale, or if the
% limits span less than a decade, MINORTICKS will be empty.
%
% [...,OVERHANG] = CALCTICKS(...) Return a 1x2 vector OVERHANG containing
% the distances (in data units) that the outermost tick labels extend from
% the lower and upper axes limits. If the chosen tick interval results in
% the outermost ticks being inset from the data limits by at least one half
% of the label size, OVERHANG will be zero.
%
%
% REMARKS
%
% Note that CALCTICKS does not draw the calculated ticks and ticklabels,
% but simply returns their values. See below for a usage example.
%
% If the actual values of LIMITS are chosen by CALCTICKS as the outermost
% ticks, those ticks will be exactly the values of LIMITS. Interior tick
% values can vary slightly from exact intervals due to floating point
% precision limitations. Ticks within 10*eps(min(abs(LIMITS))) of zero
% are rounded to zero.
%
% If the precision needed to display the tick values is greater than the
% number of characters specified by MAXCHARS, the values returned in the
% TICKLABEL strings will be truncated to MAXCHARS.
%
% To determine text size in data units, the following method can be used:
%
% First, ensure that the x or y limits of the axes are set to the desired
% value, and that the axes and parent figure are the intended size. As an
% alternative, perform the following with limits of [0 1], then multiply
% the result by the difference of the desired limits.
%
% hTest = text(1,1,'2','units','data');
% ext = get(hTest,'Extent');
% delete(hTest)
%
% % For horizontal (x) orientation:
% textSize = ext(3);
%
% % For vertical (y) orientation:
% textSize = ext(4);
%
% IMPORTANT: The text size in data units is (by definition) relative to
% the limits of the data. If the axes limits change after getting the
% text size, the value for textSize will be incorrect, and should be
% renormalized to the new limits.
% If the axes position changes (e.g. due to a figure resize), the
% value for textSize will be incorrect because the axes size (in data
% units) remains the same while both the axes size (in absolute units)
% and the text size in points (absolute units) do not. Be aware that
% events such as changing the axes limits, ticks, x or y label, title, or
% other axes properties often causes the axes to resize automatically.
%
%
% EXAMPLE
%
% Results will vary depending on monitor resolution. On a monitor running
% at 1280x1024 pixels at 96 dpi, the values shown here result in the axes'
% default xticklabels overlapping, and the yticklabels are not displayed
% with sufficient precision.
%
% % Create a figure, set limits and plot a curve
% figure('Position',[360 502 480 360])
% hAx = axes;
% set(hAx,'FontSize',12)
% s = get(hAx);
%
% xlimits = [1200 1200.003];
% ylimits = [1.2e-6 1.20003e-6];
%
% x = linspace(xlimits(1),xlimits(2),101);
% y = sin(1e4*x)*0.4*diff(ylimits) + mean(ylimits);
% plot(x,y)
%
% set(hAx,'XLim',xlimits,'YLim',ylimits)
%
% % Get ticks for the x axis using calcticks
% [xTicks,xTickLabels] = calcticks(xlimits,'x');
%
% % Plot the calculated ticks and labels
% tw = diff(ylimits)*.02;
% dy = ylimits(1)+[tw;2*tw];
% hXTicks = line(repmat(xTicks,2,1),repmat(dy,1,length(xTicks)),'Color','b');
%
% hXTickLabels = text(xTicks',repmat(dy(2),length(xTicks),1),xTickLabels,...
% 'Color','b','HorizontalAlignment','center','verticalAlignment',...
% 'bottom','FontAngle',s.FontAngle,'FontName',s.FontName,'FontSize',...
% s.FontSize,'FontWeight',s.FontWeight);
%
% % Get y ticks using calcticks
% [yTicks,yTickLabels,scaleStr] = calcticks;
%
% % Plot y ticks and labels
% tw = diff(xlimits)*.02;
% dx = xlimits(1)+[tw;2*tw];
% hYTicks = line(repmat(dx,1,length(yTicks)),repmat(yTicks,2,1),'color','r');
%
% hYTickLabels = text(repmat(dx(2),length(yTicks),1),yTicks',yTickLabels,...
% 'Color','r','HorizontalAlignment','left','VerticalAlignment',...
% 'middle','FontAngle',s.FontAngle,'FontName',s.FontName,'FontSize',...
% s.FontSize,'FontWeight',s.FontWeight);
%
% % Now, set the new values as the axes' ticks and ticklabels and delete the
% % temporary text and labels.
% set(hAx,'XTick',xticks,'XTickLabel',xticklabels,'YTick',yticks,...
% 'YTickLabel',yticklabels)
% delete([hXTicks; hXTickLabels; hYTicks; hYTickLabels])
%
% % If the scale is not displayed for the y-axis, manually place the
% % scale string
% text(xlimits(1),ylimits(2),scaleStr,'HorizontalAlignment','left',...
% 'VerticalAlignment','bottom','FontAngle',s.FontAngle,'FontName',...
% s.FontName,'FontSize',s.FontSize,'FontWeight',s.FontWeight)
%
%
% See also AXES
% $$FileInfo
% $Filename: calcticks.m
% $Path: $toolboxroot/
% $Product Name: calcticks
% $Product Release: 1.1
% $Revision: 1.1.5
% $Toolbox Name: Custom Plots Toolbox
% $$
%
% Copyright (c) 2010-2011 John Barber.
%
% Release History:
% v 1.0 : 2011-Mar-08
% - Initial release
% v 1.1 : 2011-Mar-29
% - Fixed bug that caused log-scale ticklabels to be truncated
% - Improved interval selection for log-scale ticks
% - Moved into Custom Plots Toolbox
%% Constants
% Default limit on label length (in characters)
defMaxChars = 9;
% Minimum value of the (upper) limit on label length (characters). Setting
% this value too small will cause problems.
% Note: This value does not affect the minimum length of the ticklabels.
minChars = 6;
% Default font size for exponents (assumes that font units are 'points')
defExpFontSize = 7;
% Upper limit on number of ticks returned by CALCTICKS
initMaxTicks = 11;
% Multiplier for textSize for vertical orientation when scale is 'log', to
% account for labels using exponential notation.
vertExpScale = 1.3;
%% Parse inputs
nargs = nargin;
% Handle empty input
if nargin == 0
limits = [];
orientation = 'v';
varargin = cell(0,0);
end
% Check for an axes handle as first argument
if isscalar(limits) && ishandle(limits) && strcmp(get(limits,'Type'),'axes')
hAx = limits;
if nargs == 2
limits = orientation;
orientation = [];
elseif nargs > 2
limits = orientation;
orientation = varargin{1};
varargin(1) = [];
end
nargs = nargs-1;
else
% Leave hAx empty unless we absolutely need it
hAx = [];
end
% Validate orientation first so we can get the right axes limits if needed
% Orientation: {'v'} or 'h', also 'x' or 'y'
if nargs < 2 || isempty(orientation) || ...
~any(strcmpi(orientation(1),{'h','x'}))
orientation = 'v';
axLim = 'YLim';
axScale = 'YScale';
else
orientation = 'h';
axLim = 'XLim';
axScale = 'XScale';
end
if nargs == 0 || isempty(limits)
% Get limits from an axes handle passed in as first argument, or gca.
if isempty(hAx)
hAx = gca;
end
limits = get(hAx,axLim);
elseif ~isreal(limits) || ~all(size(limits) == [1 2]) || ...
(limits(1) >= limits(2))
eID = [mfilename ':InvalidLimits'];
eStr = '''limits'' must be a 1x2 vector with limits(2) > limits(1).';
error(eID,eStr)
end
% Text size
if nargs < 3 || isempty(varargin{1})
if isempty(hAx)
hAx = gca;
end
textSize = getTextSize(limits,orientation,hAx);
else
textSize = varargin{1};
end
% Scale
if nargs < 4 || isempty(varargin{2})
if isempty(hAx)
hAx = gca;
end
scale = get(hAx,axScale);
else
scale = varargin{2};
end
% Exponent string style
if nargs < 5 || isempty(varargin{3})
separateExp = true;
else
separateExp = varargin{3};
if ischar(separateExp)
% Accept 'y(es)', 't(rue)', 'o(n)', 's(eparate)' as true
separateExp = any(strcmpi(separateExp(1),{'y','t','o','s'}));
else
separateExp = logical(separateExp(1));
end
end
% Handle expFontSize, set a flag to use or not use this value
if nargs < 6 || isempty(varargin{4})
smallExp = true;
expFontSize = defExpFontSize;
else
expFontSize = varargin{4};
if islogical(expFontSize)
smallExp = expFontSize;
expFontSize = defExpFontSize;
else
smallExp = ~isnan(expFontSize) && expFontSize > 0;
end
end
% Maximum number of characters in label string. Determines numerical
% precision displayed by the labels, and also affects the tick spacing for
% horizontal orientation.
if nargs < 7 || isempty(varargin{5}) || ~(isnumeric(varargin{5}) && ...
isscalar(varargin{5}))
maxChars = defMaxChars;
else
maxChars = varargin{5};
if maxChars < minChars
maxChars = minChars;
end
end
%% Initial calculations
% Bypass to logticks calculation if scale is 'log'
if ~isempty(scale) && strcmpi(scale(1:2),'lo')
[ticks,tickLabels,overhang,minorTicks] = logticks(limits,...
textSize,orientation,smallExp,expFontSize,maxChars,vertExpScale);
scaleStr = '';
return
else
% No minor ticks for linear scale
minorTicks = [];
end
% Data range
range = diff(limits);
% Get eps values for rounding
lEps = eps(limits(1));
uEps = eps(limits(2));
minEps = min(lEps,uEps);
% Vector of allowed tick counts
testTickCounts = 2:initMaxTicks;
% Make a list of rough intervals as a starting point
roughInts = (range./(testTickCounts-1))';
% Vector of 'nice' intervals
niceVec = [1 2 5 10];
%% Find nice intervals
% Normalize rough intervals by their scale
decRoughInts = floor(log10(roughInts));
normRoughInts = roughInts./10.^decRoughInts;
% Get the distances to nice intervals, pick the shortest
deltas = abs(repmat(normRoughInts,1,length(niceVec)) - ...
repmat(niceVec,length(normRoughInts),1));
[trash,idx] = min(deltas,[],2); %#ok<ASGLU>
% Get the nice intervals and scores
niceInts = niceVec(idx)'.*10.^decRoughInts;
% Remove duplicates
niceInts = unique(niceInts);
% Get upper and lower limits, fixed by the list of nice intervals. Round
% out to make sure we get ticks at the original limits.
lLims = floor(limits(1)./niceInts).*niceInts;
uLims = ceil(limits(2)./niceInts).*niceInts;
% Get tick counts using the list of nice intervals and limits
nTicks = floor(1 + (uLims - lLims + 10*minEps)./niceInts);
% Shrink nice limits that are outside of original limits
idx = lLims < limits(1) - 10*eps(limits(1));
nTicks(idx) = nTicks(idx)-1;
lLims(idx) = lLims(idx) + niceInts(idx);
idx = uLims > limits(2) + 10*eps(limits(1));
nTicks(idx) = nTicks(idx)-1;
uLims(idx) = uLims(idx) - niceInts(idx);
% Set values that are almost exactly the original limits to be the original
% limit value.
idx = abs(lLims - limits(1)) < 10*eps(limits(1));
lLims(idx) = limits(1);
idx = abs(uLims - limits(2)) < 10*eps(limits(2));
uLims(idx) = limits(2);
% Discard values where the limits are reversed or equal
idx = (lLims >= uLims);
lLims(idx)=[];
uLims(idx)=[];
nTicks(idx)=[];
niceInts(idx)=[];
%% Determine label size for each interval
% Get the decade span of the limits and the decade of the intervals
maxAbs = max(abs([lLims uLims]),[],2);
decMax = floor(log10(maxAbs));% - nDec;
decInts = floor(log10(niceInts));% - nDec;
% Get the number of characters needed for tick labels for normal notation
labelChars = max(decMax+1,1) + (decInts<0).*(1-decInts);
labelChars(labelChars > maxChars - 1) = maxChars - 1;
% Handle exponential notation
% Determine whether or not to use exponential notation
if separateExp
% Large numbers:
isExp = (decMax > 6) | (decMax == 6 & decInts > 0);
% Small numbers:
isExp = isExp | (decMax < -3) | (decMax == -3 & decInts < -5);
else
% Large numbers:
isExp = decMax > 6 | (decMax == 6 & decInts > 3);
% Small numbers:
isExp = isExp | (decMax < -3) | (decMax == -3 & decInts < -5);
end
% Get length of exponential labels depending on style
if separateExp
expChars = 2 + max(0,min(maxChars-3,decMax-decInts));
% scaleSignChar = decMax < 0;