Global Index (short | long) | Local contents | Local Index (short | long)
hh = clabel(cs,varargin)
CLABEL Contour plot elevation labels. CLABEL(CS,H) adds height labels to the current contour plot. The labels are rotated and inserted within the contour lines. CS and H are the contour matrix output and object handle outputs from CONTOUR, CONTOUR3, or CONTOURF. CLABEL(CS,H,V) labels just those contour levels given in vector V. The default action is to label all known contours. The label positions are selected randomly. CLABEL(CS,H,'manual') places contour labels at the locations clicked on with a mouse. Pressing the return key terminates labeling. Use the space bar to enter contours and the arrow keys to move the crosshair if no mouse is available. CLABEL(CS) or CLABEL(CS,V) or CLABEL(CS,'manual') places contour labels as above, except that the labels are drawn as plus signs on the contour with a nearby height value. H = CLABEL(...) returns handles to the TEXT (and possibly LINE) objects created. The UserData property of the TEXT objects contain the height value for each label. CLABEL(...,'text property',property_value,...) allows arbitrary TEXT property/value pairs to specified for the label strings. One special property ('LabelSpacing') is also available to specify the spacing between labels (in points). This defaults to 144, or 2 inches. Uses code by R. Pawlowicz to handle inline contour labels. Example subplot(1,3,1), [cs,h] = contour(peaks); clabel(cs,h,'labelspacing',72) subplot(1,3,2), cs = contour(peaks); clabel(cs) subplot(1,3,3), [cs,h] = contour(peaks); clabel(cs,h,'fontsize',15,'color','r','rotation',0) See also CONTOUR, CONTOUR3, CONTOURF.
This function calls | This function is called by |
---|---|
function hh = clabel(cs,varargin) % Thanks to R. Pawlowicz (IOS) rich@ios.bc.ca for the algorithm used % in 'inline_labels' so that clabel can produce inline labeling. % Copyright (c) 1984-98 by The MathWorks, Inc. % $Revision: 5.33 $ $Date: 1998/07/29 18:16:23 $ % Modified by R Pawlowicz to allow for text properties as in % extcontour code 14/5/97 % 28/10/97 - modified to work in map contouring % 9/01/98 - improved calculation of gaps for line labels % Fix by Eric Firing, efiring@soest.hawaii.edu, 4/97, to % make the rotation angles correct when XDir and/or YDir are % reverse. if nargin == 0 error('Not enough input arguments.') end if min(size(cs)) > 2 error('First input must be a valid contour description matrix.') end threeD = IsThreeD(gca); if nargin == 1, h = plus_labels(threeD,cs); else if ~isempty(varargin{1}(1)) & ishandle(varargin{1}(1)) & ... (strcmp(get(varargin{1}(1),'type'),'line') | strcmp(get(varargin{1}(1),'type'),'patch')), h = inline_labels(cs,varargin{:}); else h = plus_labels(threeD,cs,varargin{:}); end end if nargout>0, hh = h; end if ~ishold, if threeD, view(3), else view(2), end end %-------------------------------------------------------------- function H = inline_labels(CS,h,varargin) % % Draw the labels along the contours and rotated to match the local slope. % % To open up space in the contours, we rely on the order in which % the handles h are created in CONTOUR3. If CONTOUR3 changes you % might need to change the algorithm below. % Author: R. Pawlowicz IOS rich@ios.bc.ca % 12/12/94 % changes - R. Pawlowicz 14/5/97 - small bug in "that ole' % matlab magic" fixed, also another in manual selection % of locations. manual=0; v=[]; inargs=zeros(1,length(varargin)); if nargin>=3 & strcmp(varargin{1},'manual'), manual = 1; inargs(1)=1; end if ~manual & nargin>=3 & ~isstr(varargin{1}), v = varargin{1}; inargs(1)=1; end; lab_int=72*2; % label interval (points) for k=find(inargs==0), if strncmpi(varargin{k},'lab',3), inargs([k k+1])=1; lab_int=varargin{k+1}; end; end; varargin(find(inargs))=[]; if strcmp(get(h(1),'type'),'patch') & ~strcmp(get(h(1),'facecolor'),'none'), isfilled = 1; else isfilled = 0; end %% EF 4/97 if (strcmp(get(gca, 'XDir'), 'reverse')), XDir = -1; else XDir = 1; end if (strcmp(get(gca, 'YDir'), 'reverse')), YDir = -1; else YDir = 1; end %% % Compute scaling to make sure printed output looks OK. We have to go via % the figure's 'paperposition', rather than the the absolute units of the % axes 'position' since those would be absolute only if we kept the 'units' % property in some absolute units (like 'points') rather than the default % 'normalized'. UN=get(gca,'units'); if (UN(1:3)=='nor'), UN=get(gcf,'paperunits'); set(gcf,'paperunits','points'); PA=get(gcf,'paperposition'); set(gcf,'paperunits',UN); PA=PA.*[get(gca,'position')]; else set(gca,'units','points'); PA=get(gca,'pos'); set(gca,'units',UN); end % Find beginning of all lines lCS=size(CS,2); if ~isempty(get(gca,'children')), XL=get(gca,'xlim'); YL=get(gca,'ylim'); else iL=[]; k=1; XL=[Inf -Inf]; YL=[Inf -Inf]; while (k<lCS), x=CS(1,k+(1:CS(2,k))); y=CS(2,k+(1:CS(2,k))); XL=[ min([XL(1),x]) max([XL(2),x]) ]; YL=[ min([YL(1),y]) max([YL(2),y]) ]; iL=[iL k]; k=k+CS(2,k)+1; end; set(gca,'xlim',XL,'ylim',YL); end; Aspx=PA(3)/diff(XL); % To convert data coordinates to paper (we need % to do this Aspy=PA(4)/diff(YL); % to get the gaps for text the correct size) H=[]; % Set up a dummy text object from which you can get text extent info H1=text(XL(1),YL(1),'dummyarg','units','points','visible','off',varargin{:}); % Decompose contour data structure if manual mode. if manual disp(' '), disp(' Please wait a moment...') x = []; y = []; ilist = []; klist = []; plist = []; ii = 0; k = 0; n = 0; while (1) k = k + 1; ii = ii + n + 1; if ii > lCS, break, end c = CS(1,ii); n = CS(2,ii); nn = 2 .* n -1; xtemp = zeros(nn, 1); ytemp = zeros(nn, 1); xtemp(1:2:nn) = CS(1, ii+1:ii+n); xtemp(2:2:nn) = (xtemp(1:2:nn-2) + xtemp(3:2:nn)) ./ 2; ytemp(1:2:nn) = CS(2, ii+1:ii+n); ytemp(2:2:nn) = (ytemp(1:2:nn-2) + ytemp(3:2:nn)) ./ 2; x = [x; xtemp]; y = [y; ytemp]; % Keep these. ilist = [ilist; ii(ones(nn,1))]; klist = [klist; k(ones(nn,1))]; plist = [plist; (1:.5:n)']; end ax = axis; xmin = ax(1); xmax = ax(2); ymin = ax(3); ymax = ax(4); xrange = xmax - xmin; yrange = ymax - ymin; xylist = (x .* yrange + sqrt(-1) .* y .* xrange); view(2) disp(' '); disp(' Carefully select contours for labeling.') disp(' When done, press RETURN while the Graph window is the active window.') end % Get labels all at once to get the length of the longest string. % This allows us to call extent only once, thus speeding up this routine if ~manual, labels = getlabels(CS); % Get the size of the label set(H1,'string',repmat('9',1,size(labels,2)),'visible','on',varargin{:}) EX=get(H1,'extent'); set(H1,'visible','off') len_lab=EX(3)/2; end ii=1; k = 0; while (ii<lCS), if manual [xx, yy, button] = ginput(1); if isempty(button) | isequal(button,13), break, end if xx < xmin | xx > xmax, break, end if yy < ymin | yy > ymax, break, end xy = xx .* yrange + sqrt(-1) .* yy .* xrange; dist = abs(xylist - xy); [dum,f] = min(dist); if ~isempty(f) f = f(1); ii = ilist(f); k = klist(f); p = floor(plist(f)); end else k = k+1; end if ~isfilled & k>length(h), error('Not enough contour handles.'); end l=CS(2,ii); x=CS(1,ii+(1:l)); y=CS(2,ii+(1:l)); lvl=CS(1,ii); if manual lab=num2str(lvl); % Get the size of the label set(H1,'string',lab,'visible','on',varargin{:}) EX=get(H1,'extent'); set(H1,'visible','off') len_lab=EX(3)/2; else %RP - get rid of all blanks in label lab=labels(k,labels(k,:)~=' '); %RP - scale label length by string size instead of a fixed length len_lab=EX(3)/2*length(lab)/size(labels,2); end % RP28/10/97 - Contouring sometimes returns x vectors with % NaN in them - we want to handle this case! sx=x*Aspx; sy=y*Aspy; d=[0 sqrt(diff(sx).^2 +diff(sy).^2)]; % Determine the location of the NaN separated sections section = cumsum(isnan(d)); d(isnan(d))=0; d=cumsum(d); if ~manual len_contour = max(0,d(l)-3*len_lab); slop = (len_contour - floor(len_contour/lab_int)*lab_int); start = 1.5*len_lab + max(len_lab,slop)*rands(1); % Randomize start psn=[start:lab_int:d(l)-1.5*len_lab]; else psn = min(max(max(d(p),d(2)+eps*d(2)),d(1)+len_lab),d(end)-len_lab); psn = max(0,min(psn,max(d))); end lp=size(psn,2); if (lp>0) & isfinite(lvl) & ... (isempty(v) | any(abs(lvl-v)/max(eps+abs(v)) < .00001)), Ic=sum( d(ones(1,lp),:)' < psn(ones(1,l),:),1 ); Il=sum( d(ones(1,lp),:)' <= psn(ones(1,l),:)-len_lab,1 ); Ir=sum( d(ones(1,lp),:)' < psn(ones(1,l),:)+len_lab,1 ); % Check for and handle out of range values out = (Ir < 1 | Ir > length(d)-1) | ... (Il < 1 | Il > length(d)-1) | ... (Ic < 1 | Ic > length(d)-1); Ir = max(1,min(Ir,length(d)-1)); Il = max(1,min(Il,length(d)-1)); Ic = max(1,min(Ic,length(d)-1)); % For out of range values, don't remove datapoints under label Il(out) = Ic(out); Ir(out) = Ic(out); % Remove label if it isn't in the same section bad = (section(Il) ~= section(Ir)); Il(bad) = []; Ir(bad) = []; Ic(bad) = []; psn(:,bad) = []; out(bad) = []; lp = length(Il); in = ~out; if ~isempty(Il) % Endpoints of text in data coordinates wl=(d(Il+1)-psn+len_lab.*in)./(d(Il+1)-d(Il)); wr=(psn-len_lab.*in-d(Il) )./(d(Il+1)-d(Il)); xl=x(Il).*wl+x(Il+1).*wr; yl=y(Il).*wl+y(Il+1).*wr; wl=(d(Ir+1)-psn-len_lab.*in)./(d(Ir+1)-d(Ir)); wr=(psn+len_lab.*in-d(Ir) )./(d(Ir+1)-d(Ir)); xr=x(Ir).*wl+x(Ir+1).*wr; yr=y(Ir).*wl+y(Ir+1).*wr; trot=atan2( (yr-yl)*YDir*Aspy, (xr-xl)*XDir*Aspx )*180/pi; %% EF 4/97 backang=abs(trot)>90; trot(backang)=trot(backang)+180; % Text location in data coordinates wl=(d(Ic+1)-psn)./(d(Ic+1)-d(Ic)); wr=(psn-d(Ic) )./(d(Ic+1)-d(Ic)); xc=x(Ic).*wl+x(Ic+1).*wr; yc=y(Ic).*wl+y(Ic+1).*wr; % Shift label over a little if in a curvy area shiftfrac=.5; xc=xc*(1-shiftfrac)+(xr+xl)/2*shiftfrac; yc=yc*(1-shiftfrac)+(yr+yl)/2*shiftfrac; % Remove data points under the label... % First, find endpoint locations as distances along lines dr=d(Ir)+sqrt( ((xr-x(Ir))*Aspx).^2 + ((yr-y(Ir))*Aspy).^2 ); dl=d(Il)+sqrt( ((xl-x(Il))*Aspx).^2 + ((yl-y(Il))*Aspy).^2 ); % Now, remove the data points in those gaps using that % ole' Matlab magic. We use the sparse array stuff instead of % something like: % f1=zeros(1,l); f1(Il)=ones(1,lp); % because the sparse functions will sum into repeated indices, % rather than just take the last accessed element - compare % x=[0 0 0]; x([2 2])=[1 1] % with % x=full(sparse([1 1],[2 2],[1 1],1,3)) % (bug fix in original code 18/7/95 - RP) f1=full(sparse(ones(1,lp),Il,ones(1,lp),1,l)); f2=full(sparse(ones(1,lp),Ir,ones(1,lp),1,l)); irem=find(cumsum(f1)-cumsum(f2))+1; x(irem)=[]; y(irem)=[]; d(irem)=[]; l=l-size(irem,2); % Put the points in the correct order... xf=[x(1:l),xl,repmat(NaN,size(xc)),xr]; yf=[y(1:l),yl,yc,yr]; [df,If]=sort([d(1:l),dl,psn,dr]); % ...and draw. % % Here's where we assume the order of the h(k). % z = get(h(k),'zdata'); if ~isfilled, % Only modify lines or patches if unfilled set(h(k),'xdata',[xf(If) NaN],'ydata',[yf(If) NaN]) % Handle contour3 case (z won't be empty). if ~isempty(z), set(h(k),'zdata',[]) % Work around for bug in face generation % Set z to a constant while preserving the location of NaN's set(h(k),'zdata',z(1)+0*get(h(k),'xdata')) end if strcmp(get(h(k),'type'),'patch') set(h(k),'cdata',lvl+[0*xf(If) nan]) end end for jj=1:lp, % Handle contour3 case (z won't be empty). if ~isempty(z), H = [H;text(xc(jj),yc(jj),z(1),lab,'rotation',trot(jj), ... 'verticalAlignment','middle','horizontalAlignment','center',... 'clipping','on','userdata',lvl,varargin{:})]; else H = [H;text(xc(jj),yc(jj),lab,'rotation',trot(jj), ... 'verticalAlignment','middle','horizontalAlignment','center',... 'clipping','on','userdata',lvl,varargin{:})]; end end end % ~isempty(Il) else if ~isfilled, % Only modify lines or patches if unfilled % % Here's another place where we assume the order of the h(k) % set(h(k),'xdata',[x NaN],'ydata',[y NaN]) % Handle contour3 case (z won't be empty). z = get(h(k),'zdata'); if ~isempty(z), set(h(k),'zdata',[]) % Work around for bug in face generation % Set z to a constant while preserving the location of NaN's set(h(k),'zdata',z(1)+0*get(h(k),'xdata')) end if strcmp(get(h(k),'type'),'patch') set(h(k),'cdata',lvl+[0*x nan]) end end end; if ~manual ii=ii+1+CS(2,ii); end end; % delete dummy string delete(H1); %------------------------------------------------------- %------------------------------------------------------- function h = plus_labels(threeD,cs,varargin); % % Draw the labels as plus symbols next to text (v4 compatible) % % RP - 14/5/97 % Clay M. Thompson 6-7-96 % Charles R. Denham, MathWorks, 1988, 1989, 1990. cax = gca; manual = 0; choice = 0; if nargin > 2 if isstr(varargin{1}), if strcmp(varargin{1}, 'manual') varargin(1)=[]; manual=1; end; else choice = 1; v = sort(varargin{1}(:)); varargin(1)=[]; end end [mcs, ncs] = size(cs); % Find range of levels. k = 1; i = 1; while k <= ncs levels(i) = cs(1,k); i = i + 1; k = k + cs(2,k) + 1; end cmin = min(levels); cmax = max(levels); crange = max(abs(levels)); cdelta = abs(diff(levels)); cdelta = min(cdelta(cdelta > eps))/max(eps,crange); % Minimum significant change if isempty(cdelta), cdelta = 0; end % Decompose contour data structure if manual mode. if manual disp(' '), disp(' Please wait a moment...') x = []; y = []; clist = []; k = 0; n = 0; while (1) k = k + n + 1; if k > ncs, break, end c = cs(1,k); n = cs(2,k); nn = 2 .* n -1; xtemp = zeros(nn, 1); ytemp = zeros(nn, 1); xtemp(1:2:nn) = cs(1, k+1:k+n); xtemp(2:2:nn) = (xtemp(1:2:nn-2) + xtemp(3:2:nn)) ./ 2; ytemp(1:2:nn) = cs(2, k+1:k+n); ytemp(2:2:nn) = (ytemp(1:2:nn-2) + ytemp(3:2:nn)) ./ 2; x = [x; xtemp]; y = [y; ytemp]; % Keep these. clist = [clist; c .* ones(2*n-1, 1)]; end ax = axis; xmin = ax(1); xmax = ax(2); ymin = ax(3); ymax = ax(4); xrange = xmax - xmin; yrange = ymax - ymin; xylist = (x .* yrange + sqrt(-1) .* y .* xrange); view(2) disp(' '); disp(' Carefully select contours for labeling.') disp(' When done, press RETURN while the Graph window is the active window.') end k = 0; n = 0; flip = 0; h = []; while (1) % Use GINPUT and select nearest point if manual. if manual [xx, yy, button] = ginput(1); if isempty(button) | isequal(button,13), break, end if xx < xmin | xx > xmax, break, end if yy < ymin | yy > ymax, break, end xy = xx .* yrange + sqrt(-1) .* yy .* xrange; dist = abs(xylist - xy); [dum,f] = min(dist); if length(f) > 0 f = f(1); xx = x(f); yy = y(f); c = clist(f); okay = 1; else okay = 0; end end % Select a labeling point randomly if not manual. if ~manual k = k + n + 1; if k > ncs, break, end c = cs(1, k); n = cs(2, k); if choice f = find(abs(c-v)/max(eps+abs(v)) < .00001); okay = length(f) > 0; else okay = 1; end if okay r = rands(1); j = fix(r.* (n - 1)) + 1; if flip, j = n - j; end flip = ~flip; if n == 1 % if there is only one point xx = cs(1, j+k); yy = cs(2, j+k); else x1 = cs(1, j+k); y1 = cs(2, j+k); x2 = cs(1, j+k+1); y2 = cs(2, j+k+1); xx = (x1 + x2) ./ 2; yy = (y1 + y2) ./ 2; % Test was here; removed. end end end % Label the point. if okay % Set tiny labels to zero. if abs(c) <= 10*eps*crange, c = 0; end % Determine format string number of digits if cdelta > 0, ndigits = max(3,ceil(-log10(cdelta))); else ndigits = 3; end s = num2str(c,ndigits); hl = line('xdata',xx,'ydata',yy,'marker','+','erasemode','none'); ht = text(xx, yy, s, 'verticalalignment', 'bottom', ... 'horizontalalignment', 'left','erasemode','none', ... 'clipping','on','userdata',c,varargin{:}); if threeD, set(hl,'zdata',c); set(ht,'position',[xx yy c]); end h = [h;hl]; h = [h;ht]; end end %------------------------------------------------------- %------------------------------------------------------- function labels = getlabels(CS) %GETLABELS Get contour labels v = []; i =1; while i < size(CS,2), v = [v,CS(1,i)]; i = i+CS(2,i)+1; end labels = num2str(v'); %--------------------------------------------------- function threeD = IsThreeD(cax) %ISTHREED True for a contour3 plot hp = findobj(cax,'type','patch'); if isempty(hp), hp = findobj(gca,'type','line'); end if ~isempty(hp), % Assume a contour3 plot if z data not empty threeD = ~isempty(get(hp(1),'zdata')); else threeD = 0; end %---------------------------------------- function r = rands(n) %RANDS Stateless rand % R = RANDS(N) is the same as calling RAND(INP) except % that the state of the random number generator is unaffected. Currstate=rand('state'); rand('state',sum(100*clock)); r = rand(n); rand('state',Currstate); % resets to original state.