├── .gitignore ├── FEX-function_handle ├── .gitignore ├── License.txt ├── README.md └── function_handle.m ├── LICENSE.txt ├── README.html ├── README.m ├── README.md ├── arclength ├── .gitignore ├── arclength.m └── license.txt ├── contourcs ├── .gitignore ├── contourcs.m └── license.txt ├── contourfcmap ├── .gitignore └── contourfcmap.m ├── distance2curve ├── .gitignore ├── distance2curve.m └── license.txt ├── fillnan ├── .gitignore └── fillnan.m ├── interparc ├── .gitignore ├── interparc.m └── license.txt ├── multiplepolyint ├── .gitignore └── multiplepolyint.m ├── pcolorbar ├── .gitignore └── pcolorbar.m └── readmeExtras ├── README_01.png ├── README_02.png ├── README_03.png ├── README_04.png ├── README_05.png ├── README_06.png ├── README_07.png ├── README_08.png ├── README_09.png ├── README_10.png └── README_11.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | ehthumbs.db 37 | Thumbs.db -------------------------------------------------------------------------------- /FEX-function_handle/.gitignore: -------------------------------------------------------------------------------- 1 | slprj/ 2 | *.mexw32 3 | *.mexglx 4 | *.mexmaci 5 | *.mexw64 6 | *.mexmaci64 7 | *.mexa64 8 | *.zip 9 | *.m~ 10 | *.asv 11 | 12 | -------------------------------------------------------------------------------- /FEX-function_handle/License.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018, Rody Oldenhuis 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | The views and conclusions contained in the software and documentation are those 25 | of the authors and should not be interpreted as representing official policies, 26 | either expressed or implied, of this project. 27 | -------------------------------------------------------------------------------- /FEX-function_handle/README.md: -------------------------------------------------------------------------------- 1 | [![View Constructor for function_handles on File Exchange](https://www.mathworks.com/matlabcentral/images/matlab-file-exchange.svg)](https://www.mathworks.com/matlabcentral/fileexchange/45941-constructor-for-function_handles) 2 | 3 | [![Donate to Rody](https://i.stack.imgur.com/bneea.png)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4M7RMVNMKAXXQ&source=url) 4 | 5 | # FEX-function_handle 6 | 7 | If you ever found yourself in a situation where you could not or didn't want to add a directory to the MATLAB search path, but still needed speedy access to a function in that directory, this file is for you. 8 | In such cases, having to change directories to gain access to the function is not the best solution: you'd always have to take care to change the current path back to what it was (even on error). Moreover, performance in those cases can be poor if you have to call this function very often. 9 | 10 | FUNCTION_HANDLE allows you to create function handles which can successfully be evaluated without loss of performance, even if the function the handle points to is not on the MATLAB search path. 11 | 12 | While there often are better ways to accomplish this sort of task (package directories, symbolic links, etc.), there are a few niche cases where these solutions are simply more involved than using this FUNCTION_HANDLE constructor. 13 | 14 | Note that FUNCTION_HANDLE overloads a function present in standard MATLAB. This 'native' function is nothing more than documentation (linked to in the help for this constructor) and an error message which says that you cannot use the function to construct handles. As this is exactly what FUNCTION_HANDLE implements, this shadowing is desirable. 15 | 16 | Example session: 17 | 18 | >> F = function_handle('./path/to/function/myFcn.m') 19 | F = 20 | @myFcn 21 | 22 | >> A = function_handle(... 23 | {@cos, '../dir/not/on/path/myFunction.m'}) 24 | A = 25 | @cos @myFunction 26 | 27 | >> A{1}(pi) 28 | ans = 29 | -1 30 | 31 | >> functions(A{1}) 32 | ans = 33 | function: 'min' 34 | type: 'simple' 35 | file: '' 36 | 37 | >> functions(A{2}) 38 | ans = 39 | function: 'myFunction' 40 | type: 'simple' 41 | file: '/fullpath/dir/not/on/path/myFunction.m' 42 | 43 | If you like this work, please consider [a donation](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4M7RMVNMKAXXQ&source=url). 44 | -------------------------------------------------------------------------------- /FEX-function_handle/function_handle.m: -------------------------------------------------------------------------------- 1 | function h = function_handle(fcn) 2 | % FUNCTION_HANDLE Construct function handle from string or 3 | % cell array of strings 4 | % 5 | % FUNCTION_HANDLE constructs a function_handle for built-in or 6 | % user-defined functions. 7 | % 8 | % When constructing a function_handle directly with @ or str2func, 10 | % it is only possible to succesfully evaluate the resulting handle when 11 | % the function the handle refers to was on the MATLAB search at the time 12 | % the handle was created. To create valid handles to arbitrarty 13 | % functions possibly not on the search path, use the FUNCTION_HANDLE 14 | % constructor. 15 | % 16 | % F = FUNCTION_HANDLE(fcn) for string or cell string [fcn] creates a 17 | % function_handle or cell array of function handles for each of the 18 | % handles or strings in [fcn]. Any string in [fcn] may include the 19 | % path to the function of interest. An error is issued in case the 20 | % path information is incorrect, of the specified file is not an 21 | % executable MATLAB function. 22 | % 23 | % EXAMPLES: 24 | % 25 | % >> F = function_handle('./path/to/function/myFcn.m') 26 | % F = 27 | % @myFcn 28 | % 29 | % >> A = function_handle(... 30 | % {@cos, '../dir/not/on/MATLAB/path/myFunction.m'}) 31 | % A = 32 | % @cos @myFunction 33 | % 34 | % >> A{1}(pi) 35 | % ans = 36 | % -1 37 | % 38 | % >> functions(A{1}) 39 | % ans = 40 | % function: 'min' 41 | % type: 'simple' 42 | % file: '' 43 | % 44 | % >> functions(A{2}) 45 | % ans = 46 | % function: 'myFunction' 47 | % type: 'simple' 48 | % file: '/fullpath/dir/not/on/MATLAB/path/myFunction.m' 49 | % 50 | % See also function_handle (built-in), str2func, functions. 51 | 52 | 53 | % Please report bugs and inquiries to: 54 | % 55 | % Name : Rody P.S. Oldenhuis 56 | % E-mail : oldenhuis@gmail.com 57 | % Licence : 2-clause BSD (See License.txt) 58 | 59 | 60 | % Changelog 61 | %{ 62 | 2014/March/19 (Rody Oldenhuis) 63 | - NEW: first version 64 | %} 65 | 66 | 67 | % If you find this work useful, please consider a donation: 68 | % https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6G3S5UYM7HJ3N 69 | 70 | %% Initialize 71 | 72 | % Quick exit 73 | if isa(fcn, 'function_handle') 74 | h = fcn; return; end 75 | 76 | if ~iscell(fcn) 77 | fcn = {fcn}; end 78 | 79 | % Extract everything that already is a function handle 80 | h = cell(size(fcn)); 81 | F = cellfun('isclass', fcn, 'function_handle'); 82 | h(F) = fcn(F); 83 | if all(F) 84 | return; end 85 | 86 | % Continue with the ones specified as strings 87 | fcn = fcn(~F); 88 | if ~iscellstr(fcn) 89 | throwAsCaller(MException(... 90 | 'function_handle:invalid_objects',... 91 | 'Invalid types detected in input. Expected types are ''function_handle'' or ''char''.')); 92 | end 93 | hF = cell(size(fcn)); 94 | 95 | %% Get to work 96 | 97 | % Make sure we always end up where we started 98 | prevDir = pwd; 99 | clean__ = onCleanup(@(~)cd(prevDir)); 100 | 101 | for ii = 1:numel(fcn) 102 | 103 | % Valid inputs 104 | if any(exist(fcn{ii})==[2 3 5 6 8]) %#ok 105 | 106 | [pth,name] = fileparts(fcn{ii}); 107 | 108 | % non-builtin 109 | if ~isempty(pth) 110 | if exist(pth,'dir')==7 111 | cd(pth); 112 | hF{ii} = str2func(['@' name]); 113 | cd(prevDir); 114 | else 115 | throwAsCaller(MException(... 116 | 'function_handle:dir_not_found',... 117 | 'Directory ''%s'' not found.', pth)); 118 | end 119 | 120 | % builtin 121 | elseif exist(fcn{ii},'builtin')==5 122 | hF{ii} = str2func(fcn{ii}); 123 | 124 | % unrecognized 125 | else 126 | throwAsCaller(MException(... 127 | 'function_handle:fcn_not_found',... 128 | ['Function ''%s'' is not on the MATLAB search path, and does not seem ',... 129 | 'to be a builtin.'], name)); 130 | end 131 | 132 | % Invalid input 133 | else 134 | throwAsCaller(MException(... 135 | 'function_handle:fcn_invalid',... 136 | 'Function or file ''%s'' not found.', fcn{ii})); 137 | end 138 | end 139 | 140 | % Final assignment 141 | h(~F) = hF; 142 | 143 | % Make output more intuitive 144 | if numel(h)==1 145 | h = h{1}; end 146 | 147 | end 148 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Kelly Kearney 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | contourfcmap.m: filled contour plot with precise colormap

contourfcmap.m: filled contour plot with precise colormap

Author: Kelly Kearney

This repository includes the code for the contourfcmap.m Matlab function, along with all dependent functions required to run it.

This function creates a shaded contour map, similar to that created by the contourf function. However, the relationship between a contourf plot and its colormap (i.e. exactly which color corresponds to each contour interval), can often be confusing and inconsistent. This function instead allows the user to specify exactly which colors to use in each interval, and also to choose colors for regions that exceed the contour line limits.

Contents

Getting started

Prerequisites

This function requires Matlab R14 or later.

Downloading and installation

This code can be downloaded from Github or the MatlabCentral File Exchange. The File Exchange entry is updated daily from the GitHub repository.

Matlab Search Path

The following folders need to be added to your Matlab Search path (via addpath, pathtool, etc.):

contourfcmap-pkg/FEX-function_handle
 70 | contourfcmap-pkg/arclength
 71 | contourfcmap-pkg/contourcs
 72 | contourfcmap-pkg/contourfcmap
 73 | contourfcmap-pkg/distance2curve
 74 | contourfcmap-pkg/fillnan
 75 | contourfcmap-pkg/interparc
 76 | contourfcmap-pkg/minmax
 77 | contourfcmap-pkg/multiplepolyint
 78 | contourfcmap-pkg/parsepv
 79 | contourfcmap-pkg/pcolorbar
 80 | 

Syntax

contourfcmap(x,y,z,clev,cmap)
 81 | contourfcmap(x,y,z,clev,cmap, Name, Value)
 82 | h = contourfcmap(...)
 83 | 

Description

contourfcmap(x,y,z,clev,cmap) plots a filled contour plot of the matrix z with coordinates x and y. z must be at least a 2x2 matrix; x and y can either be matrices defining the grid for z, or vectors that correspond to the column and row coordinates, respectively. clev is an n x 1 vector defining the contour line levels, and cmap is an n-1 x 1 colormap array defining the colors to be used between each of the contour levels.

contourfcmap(x,y,z,clev,cmap, 'lo', lo) indicates the color value lo (a 1 x 3 RGB array) to be used for any region with data lower than the first contour level. Default is white.

contourfcmap(x,y,z,clev,cmap, 'hi', hi) indicates the color value hi (a 1 x 3 RGB array) to be used for any region with data higher than the last contour level. Default is white.

contourfcmap(x,y,z,clev,cmap, 'method', method) allows you to switch between the 'recolor' and 'calccontour' algorithms (see below for details). Default is 'recolor' (though I now recommend 'calccontour' for R2017b or later).

contourfcmap(x,y,z,clev,cmap, 'cbarloc', cbarloc) adds a psuedo-colorbar using the colorbar location indicator cbarloc. Default is no colorbar.

contourfcmap(x,y,z,clev,cmap, 'evencb', evencb) indicates whether to space the colors on the colorbar evenly (evencb = true) or to size them according to the clev values (evencb = false). Default is false.

h = contourfcmap(...) returns a structure h whose fields hold the colorbar axis handle structure (h.cb, see pcolorbar.m for details), contour object (h.h), contour matrix (h.c), and patch object (h.p).

Examples

First we'll plot a contourf plot using the standard Matlab functions. Without labeling the contour lines, it's difficult to tell which values in the colorbar correspond to the color intervals in the contourf plot.

count = 0;
 84 | 
 85 | % Data
 86 | 
 87 | [x,y] = meshgrid(linspace(0,1,100));
 88 | z = peaks(100);
 89 | clev = [-5 -3 -2:.5:2 3 5];
 90 | 
 91 | % Set up axes
 92 | 
 93 | h.fig = figure('color', 'w');
 94 | h.ax(1) = axes('position', [0.05  0.25 0.425 0.5]);
 95 | h.ax(2) = axes('position', [0.525 0.25 0.425 0.5]);
 96 | 
 97 | % Plot
 98 | 
 99 | axes(h.ax(1));
100 | contourf(x,y,z,clev);
101 | cb = colorbar('eastoutside');
102 | colormap(h.ax(1), jet);
103 | title(h.ax(1), 'contourf');
104 | 

Using contourfcmap, we can set the colors explictly, so it's much easier to tell exactly which color corresponds to which value.

axes(h.ax(2));
105 | hc = contourfcmap(x,y,z,clev,jet(12), ...
106 |      'lo', [.8 .8 .8], ...
107 |      'hi', [.2 .2 .2], ...
108 |      'cbarloc', 'eastoutside', ...
109 |      'method', 'calccontour');
110 | title(h.ax(2), 'contourfcmap');
111 | 

If you prefer, you can set the colorbar to show the contour intervals evenly-spaced, even if the values aren't.

delete(hc.cb.cb);
112 | delete(hc.cb.ax);
113 | cla;
114 | 
115 | hc = contourfcmap(x,y,z,clev,jet(12), ...
116 |      'lo', [.8 .8 .8], ...
117 |      'hi', [.2 .2 .2], ...
118 |      'cbarloc', 'eastoutside', ...
119 |      'method', 'calccontour', ...
120 |      'evencb', true);
121 | title(h.ax(2), 'contourfcmap');
122 | 

The algorithms behind contourfcmap

This function began its life as a very simple function back in 2010. At that time, it was just a wrapper around contourf. A contour object in Maltab circa 2010 was a combination of some lines (the contour lines) and some patches (the shaded regions), and my function just looped over those patches and assigned new colors to their CData properties.

Then the 2014b release came along, and with it, a complete overhaul of Matlab's graphics objects. Under these new second-generation handle graphics (i.e. HG2), contour objects became more complex. They now were composed of TriangleStrips, a low-level, mostly-undocumented graphics object that really wasn't designed to be tampered with.

I updated my function to repeat its old processes, looping over the contours and changing the colors. But those color changes weren't very permanent. Changing the colormap, adding a colorbar, and most frustratingly, printing figures to file (using anything that relies on the print command, including print, saveas, export_fig, etc) undoes it. My function was pretty useless if I couldn't get changes to stick.

So in 2014, I introduced a new algorithm to contourfcmap, the 'calccontour' method. This version still uses the contour function to do the heavy lifting of the contouring calculations (contouring is hard!), but then tries to draw the shaded region using patches, as in the old HG1 version. I originally considered this just a hacky stand-in until I could get recoloring to stick. It worked pretty well for simple datasets, but fell apart for more complicated ones, especially those involving NaNs and non-cartesian grids. But the recoloring option reset issue has persisted through newer releases of Matlab (actually, it's gotten worse).

I've now (March 2018) just completed an overhaul of the calccontour option. This update may break back-compatibility with older code that uses contourfcmap, since I've modified some of the output variables (though it should still support all the older syntax in terms of input variables). I've expanded the code to be much more robust to non-cartesian grids and NaNs, and with the introduction of polyshape objects, the polygon calculations no longer require the Mapping Toolbox. Though still a bit slow to plot, this algorithm also fixes a few issues with the way Matlab colors (or rather, doesn't color) lower-than-lowest-contour regions. And finally, I've altered the colorbar code to rely on my pcolorbar function, which provides for more robust resizing (similar to a real colorbar) when one makes changes to the axis.

This example walks you through my thought process with this algorithm, and some of the issues with contourf that I'm trying to address.

We'll start with the peaks data:

close all;
123 | figure;
124 | 
125 | [x,y,z] = peaks;
126 | 
127 | hs = scatter(x(:),y(:),10,z(:),'filled');
128 | hcb = colorbar;
129 | set(gca, 'clim', [-15 15], 'xlim', [-4.5 4.5], 'ylim', [-4.5 4.5]);
130 | hold on;
131 | 

Most of Matlab's example contour data assumes a cartesian grid (i.e. data aligned along the x- and y-axes, like an image). But it doesn't need to be... any structured grid will work. Let's add some skew and rotation, to make sure this example covers the more complicated cases:

y = y + sin(x);
132 | th = pi/3;
133 | R = [cos(th) -sin(th); sin(th) cos(th)];
134 | xy = R * [x(:) y(:)]';
135 | x = reshape(xy(1,:), size(x));
136 | y = reshape(xy(2,:), size(y));
137 | 
138 | delete(hs);
139 | hs = scatter(x(:),y(:),10,z(:),'filled');
140 | 

Contour data can also include NaNs. Sometimes these represent a missing data point or two. But they can also occur in bigger blocks; for example, a map of ocean data might represent land with NaNs. We'll add both enclosed (surrounded by data) and unenclosed (connecting to side of grid) NaNs, and make sure we have some extra data islands of high and low data in there too (you'll see why in a bit):

z(z < 0.2 & z > 0) = NaN;
141 | z(1:3,1:3) = -15;
142 | z(end-3:end,end-3:end) = 15;
143 | z(25:30, 1:2) = -15;
144 | 
145 | delete(hs);
146 | hs = scatter(x(:),y(:),10,z(:),'filled');
147 | 

Let's say we want to plot some contours, with specific colors between each interval. We can show this by plotting a scatter plot of the discretized values:

% Discretize
148 | 
149 | bin = discretize(z, clev);
150 | bin(z < clev(1)) = 0;
151 | bin(z > clev(end)) = length(clev)+1;
152 | 
153 | % Colors
154 | 
155 | clev = [-5 -3 -2:.5:2 3 5];
156 | cmap = [...
157 |       0.65098      0.80784       0.8902
158 |       0.12157      0.47059      0.70588
159 |       0.69804      0.87451      0.54118
160 |           0.2      0.62745      0.17255
161 |       0.98431      0.60392          0.6
162 |        0.8902      0.10196       0.1098
163 |       0.99216      0.74902      0.43529
164 |             1      0.49804            0
165 |       0.79216      0.69804      0.83922
166 |       0.41569      0.23922      0.60392
167 |             1            1          0.6
168 |       0.69412      0.34902      0.15686];
169 | lo = [0.57255      0.58431      0.56863];
170 | hi = [0.84706      0.86275      0.83922];
171 | 
172 | % Plot
173 | 
174 | delete(hs);
175 | hs = scatter(x(:),y(:),10,bin(:),'filled');
176 | colormap([lo; cmap; hi]);
177 | set(gca, 'clim', [-0.5 length(clev)+0.5]);
178 | 
179 | tklabel = ['lo' ...
180 |     arrayfun(@(a,b) sprintf('%.1f - %.1f',a,b), ...
181 |              clev(1:end-1), clev(2:end), 'uni', 0) ...
182 |     'hi'];
183 | set(hcb, 'ticks', 0:length(clev), 'ticklabels', tklabel);
184 | 

We can try to use the same discretization trick to try to get our desired filled contour plot.

delete(hs);
185 | [cc, hc] = contourf(x,y,bin,0.5:1:length(clev));
186 | 

But that sacrifices the resolution of the contouring. We can also try fiddle with the colormap, trying to get the colors to match our contour levels. I do this via the cptcmap function.

loval = clev(1) - (clev(end)-clev(1))*0.1;
187 | hival = clev(end) + (clev(end)-clev(1))*0.1;
188 | 
189 | ctable = [[loval clev]' [lo;cmap;hi]*255 [clev hival]' [lo;cmap;hi]*255]';
190 | fid = fopen('cmaptemp.cpt', 'wt');
191 | fprintf(fid, '%.2f %.0f %.0f %.0f %.2f %.0f %.0f %.0f\n', ctable);
192 | fclose(fid);
193 | [cmap2, lims] = cptcmap('cmaptemp');
194 | delete('cmaptemp.cpt');
195 | 
196 | delete(hc);
197 | [cc, hc] = contourf(x,y,z,clev);
198 | set(gca, 'clim', lims);
199 | colormap(cmap2);
200 | set(hcb, 'Ticks', clev, 'TickLabelsMode', 'auto');
201 | 

That mostly works, with a few drawbacks. First, tweaking the colormap to look like it has uneven color intervals requires some manaul calculation. Even with cptcmap, you have to do some setup ahead of time. Also, if we add the scatter plot on top, we'll see a few issues:

hs = scatter(x(:), y(:), 10, z(:), 'filled');
202 | set(hs, 'markeredgecolor', 'w');
203 | 

Most of the regions match the dots... except where there should be dark gray contours, i.e. where the data is lower than the specified lowest contour. Depending on your Matlab version, when you run this, the enclosed circle on the right may or may not be shaded. In all Matlab versions, the unenclosed areas (the bits that hit up against the wall of the grid) are unshaded.

So this is where the extra calculations in contourfcmap come in handy:

delete(hs);
204 | delete(hc);
205 | delete(hcb);
206 | 
207 | h = contourfcmap(x,y,z,clev,cmap, ...
208 |     'lo', lo, ...
209 |     'hi', hi, ...
210 |     'cbarloc', 'eastoutside', ...
211 |     'method', 'calccontour');
212 | 

Verdict: It's slower than a simple contour plot. But it colors the patches properly, including the lower-than-lowest-contour regions. And there's no need to do any tricky colormap calculations. If your application needs extremely fast rendering, and the lower-than-lowest thing isn't a problem for you, you might be better off using one of the tricks above. Otherwise, contourfcmap should be the easier solution.

Contributions

Community contributions to this package are welcome!

To report bugs, please submit an issue on GitHub and include:

  • your operating system
  • your version of Matlab and all relevant toolboxes (type ver at the Matlab command line to get this info)
  • code/data to reproduce the error or buggy behavior, and the full text of any error messages received

Please also feel free to submit enhancement requests, or to send pull requests (via GitHub) for bug fixes or new features.

I do monitor the MatlabCentral FileExchange entry for any issues raised in the comments, but would prefer to track issues on GitHub.

581 | -------------------------------------------------------------------------------- /README.m: -------------------------------------------------------------------------------- 1 | %% |contourfcmap.m|: filled contour plot with precise colormap 2 | % Author: Kelly Kearney 3 | % 4 | % This repository includes the code for the |contourfcmap.m| Matlab function, 5 | % along with all dependent functions required to run it. 6 | % 7 | % This function creates a shaded contour map, similar to that created by 8 | % the contourf function. However, the relationship between a contourf plot 9 | % and its colormap (i.e. exactly which color corresponds to each contour 10 | % interval), can often be confusing and inconsistent. This 11 | % function instead allows the user to specify exactly which colors to use 12 | % in each interval, and also to choose colors for regions that exceed the 13 | % contour line limits. 14 | % 15 | %% Getting started 16 | % 17 | % *Prerequisites* 18 | % 19 | % This function requires Matlab R14 or later. 20 | % 21 | % *Downloading and installation* 22 | % 23 | % This code can be downloaded from 24 | % or the 25 | % . The File Exchange entry is updated daily 27 | % from the GitHub repository. 28 | % 29 | % *Matlab Search Path* 30 | % 31 | % The following folders need to be added to your Matlab Search path (via 32 | % |addpath|, |pathtool|, etc.): 33 | % 34 | % contourfcmap-pkg/FEX-function_handle 35 | % contourfcmap-pkg/arclength 36 | % contourfcmap-pkg/contourcs 37 | % contourfcmap-pkg/contourfcmap 38 | % contourfcmap-pkg/distance2curve 39 | % contourfcmap-pkg/fillnan 40 | % contourfcmap-pkg/interparc 41 | % contourfcmap-pkg/minmax 42 | % contourfcmap-pkg/multiplepolyint 43 | % contourfcmap-pkg/parsepv 44 | % contourfcmap-pkg/pcolorbar 45 | 46 | %% Syntax 47 | % 48 | % contourfcmap(x,y,z,clev,cmap) 49 | % contourfcmap(x,y,z,clev,cmap, Name, Value) 50 | % h = contourfcmap(...) 51 | 52 | %% Description 53 | % 54 | % |contourfcmap(x,y,z,clev,cmap)| plots a filled contour plot of the matrix 55 | % |z| with coordinates |x| and |y|. |z| must be at least a 2x2 matrix; |x| 56 | % and |y| can either be matrices defining the grid for |z|, or vectors that 57 | % correspond to the column and row coordinates, respectively. |clev| is an 58 | % n x 1 vector defining the contour line levels, and |cmap| is an n-1 x 1 59 | % colormap array defining the colors to be used between each of the contour 60 | % levels. 61 | % 62 | % |contourfcmap(x,y,z,clev,cmap, 'lo', lo)| indicates the color value |lo| 63 | % (a 1 x 3 RGB array) to be used for any region with data lower than the 64 | % first contour level. Default is white. 65 | % 66 | % |contourfcmap(x,y,z,clev,cmap, 'hi', hi)| indicates the color value |hi| 67 | % (a 1 x 3 RGB array) to be used for any region with data higher than the 68 | % last contour level. Default is white. 69 | % 70 | % |contourfcmap(x,y,z,clev,cmap, 'method', method)| allows 71 | % you to switch between the 'recolor' and 'calccontour' algorithms (see 72 | % below for details). Default is 'recolor' (though I now recommend 73 | % 'calccontour' for R2017b or later). 74 | % 75 | % |contourfcmap(x,y,z,clev,cmap, 'cbarloc', cbarloc)| adds a 76 | % psuedo-colorbar using the colorbar location indicator |cbarloc|. Default 77 | % is no colorbar. 78 | % 79 | % |contourfcmap(x,y,z,clev,cmap, 'evencb', evencb)| indicates whether to 80 | % space the colors on the colorbar evenly (evencb = true) or to size them 81 | % according to the clev values (evencb = false). Default is false. 82 | % 83 | % |h = contourfcmap(...)| returns a structure |h| whose fields hold the 84 | % colorbar axis handle structure (|h.cb|, see |pcolorbar.m| for details), 85 | % contour object (|h.h|), contour matrix (|h.c|), and patch object (|h.p|). 86 | 87 | 88 | %% Examples 89 | % 90 | % First we'll plot a contourf plot using the standard Matlab functions. 91 | % Without labeling the contour lines, it's difficult to tell which values 92 | % in the colorbar correspond to the color intervals in the contourf plot. 93 | 94 | count = 0; 95 | 96 | % Data 97 | 98 | [x,y] = meshgrid(linspace(0,1,100)); 99 | z = peaks(100); 100 | clev = [-5 -3 -2:.5:2 3 5]; 101 | 102 | % Set up axes 103 | 104 | h.fig = figure('color', 'w'); 105 | h.ax(1) = axes('position', [0.05 0.25 0.425 0.5]); 106 | h.ax(2) = axes('position', [0.525 0.25 0.425 0.5]); 107 | 108 | % Plot 109 | 110 | axes(h.ax(1)); 111 | contourf(x,y,z,clev); 112 | cb = colorbar('eastoutside'); 113 | colormap(h.ax(1), jet); 114 | title(h.ax(1), 'contourf'); 115 | 116 | %% 117 | % Using contourfcmap, we can set the colors explictly, so it's much easier 118 | % to tell exactly which color corresponds to which value. 119 | 120 | axes(h.ax(2)); 121 | hc = contourfcmap(x,y,z,clev,jet(12), ... 122 | 'lo', [.8 .8 .8], ... 123 | 'hi', [.2 .2 .2], ... 124 | 'cbarloc', 'eastoutside', ... 125 | 'method', 'calccontour'); 126 | title(h.ax(2), 'contourfcmap'); 127 | 128 | %% 129 | % If you prefer, you can set the colorbar to show the contour intervals 130 | % evenly-spaced, even if the values aren't. 131 | 132 | delete(hc.cb.cb); 133 | delete(hc.cb.ax); 134 | cla; 135 | 136 | hc = contourfcmap(x,y,z,clev,jet(12), ... 137 | 'lo', [.8 .8 .8], ... 138 | 'hi', [.2 .2 .2], ... 139 | 'cbarloc', 'eastoutside', ... 140 | 'method', 'calccontour', ... 141 | 'evencb', true); 142 | title(h.ax(2), 'contourfcmap'); 143 | 144 | %% The algorithms behind contourfcmap 145 | % 146 | % This function began its life as a very simple function back in 2010. At 147 | % that time, it was just a wrapper around |contourf|. A contour 148 | % object in Maltab circa 2010 was a combination of some lines (the 149 | % contour lines) and some patches (the shaded regions), and my function 150 | % just looped over those patches and assigned new colors to their CData 151 | % properties. 152 | % 153 | % Then the 2014b release came along, and with it, a complete overhaul of 154 | % Matlab's graphics objects. Under these new second-generation handle 155 | % graphics (i.e. HG2), contour objects became more complex. They now were 156 | % composed of TriangleStrips, a low-level, mostly-undocumented graphics 157 | % object that really wasn't designed to be tampered with. 158 | % 159 | % I updated my function to repeat its old processes, looping over the 160 | % contours and changing the colors. But those color changes weren't very 161 | % permanent. Changing the colormap, adding a colorbar, and most 162 | % frustratingly, printing figures to file (using anything that relies on 163 | % the print command, including print, saveas, export_fig, etc) undoes it. 164 | % My function was pretty useless if I couldn't get changes to stick. 165 | % 166 | % So in 2014, I introduced a new algorithm to contourfcmap, the 'calccontour' 167 | % method. This version still uses the contour function to do the heavy 168 | % lifting of the contouring calculations (contouring is hard!), but then tries to draw the 169 | % shaded region using patches, as in the old HG1 version. I originally 170 | % considered this just a hacky stand-in until I could get recoloring to 171 | % stick. It worked pretty well for simple datasets, but fell apart for 172 | % more complicated ones, especially those involving NaNs and non-cartesian 173 | % grids. But the recoloring option reset issue has persisted through newer 174 | % releases of Matlab (actually, it's gotten worse). 175 | % 176 | % I've now (March 2018) just completed an overhaul of the calccontour 177 | % option. This update may break back-compatibility with older code that 178 | % uses contourfcmap, since I've modified some of the output variables 179 | % (though it should still support all the older syntax in terms of input 180 | % variables). I've expanded the code to be much more robust to non-cartesian 181 | % grids and NaNs, and with the introduction of polyshape objects, the 182 | % polygon calculations no longer require the Mapping Toolbox. Though still 183 | % a bit slow to plot, this algorithm also fixes a few issues with the way 184 | % Matlab colors (or rather, doesn't color) lower-than-lowest-contour 185 | % regions. And finally, I've altered the colorbar code to rely on my 186 | % pcolorbar function, which provides for more robust resizing (similar to a 187 | % real colorbar) when one makes changes to the axis. 188 | % 189 | % This example walks you through my thought process with this algorithm, 190 | % and some of the issues with contourf that I'm trying to address. 191 | % 192 | % We'll start with the peaks data: 193 | 194 | close all; 195 | figure; 196 | 197 | [x,y,z] = peaks; 198 | 199 | hs = scatter(x(:),y(:),10,z(:),'filled'); 200 | hcb = colorbar; 201 | set(gca, 'clim', [-15 15], 'xlim', [-4.5 4.5], 'ylim', [-4.5 4.5]); 202 | hold on; 203 | 204 | %% 205 | % Most of Matlab's example contour data assumes a cartesian grid (i.e. data 206 | % aligned along the x- and y-axes, like an image). But it doesn't need to 207 | % be... any structured grid will work. Let's add some skew and rotation, to 208 | % make sure this example covers the more complicated cases: 209 | 210 | y = y + sin(x); 211 | th = pi/3; 212 | R = [cos(th) -sin(th); sin(th) cos(th)]; 213 | xy = R * [x(:) y(:)]'; 214 | x = reshape(xy(1,:), size(x)); 215 | y = reshape(xy(2,:), size(y)); 216 | 217 | delete(hs); 218 | hs = scatter(x(:),y(:),10,z(:),'filled'); 219 | 220 | %% 221 | % Contour data can also include NaNs. Sometimes these represent a missing 222 | % data point or two. But they can also occur in bigger blocks; for 223 | % example, a map of ocean data might represent land with NaNs. We'll add 224 | % both enclosed (surrounded by data) and unenclosed (connecting to side of 225 | % grid) NaNs, and make sure we have some extra data islands of high and low 226 | % data in there too (you'll see why in a bit): 227 | 228 | z(z < 0.2 & z > 0) = NaN; 229 | z(1:3,1:3) = -15; 230 | z(end-3:end,end-3:end) = 15; 231 | z(25:30, 1:2) = -15; 232 | 233 | delete(hs); 234 | hs = scatter(x(:),y(:),10,z(:),'filled'); 235 | 236 | %% 237 | % Let's say we want to plot some contours, with specific colors between 238 | % each interval. We can show this by plotting a scatter plot of the 239 | % discretized values: 240 | 241 | % Discretize 242 | 243 | bin = discretize(z, clev); 244 | bin(z < clev(1)) = 0; 245 | bin(z > clev(end)) = length(clev)+1; 246 | 247 | % Colors 248 | 249 | clev = [-5 -3 -2:.5:2 3 5]; 250 | cmap = [... 251 | 0.65098 0.80784 0.8902 252 | 0.12157 0.47059 0.70588 253 | 0.69804 0.87451 0.54118 254 | 0.2 0.62745 0.17255 255 | 0.98431 0.60392 0.6 256 | 0.8902 0.10196 0.1098 257 | 0.99216 0.74902 0.43529 258 | 1 0.49804 0 259 | 0.79216 0.69804 0.83922 260 | 0.41569 0.23922 0.60392 261 | 1 1 0.6 262 | 0.69412 0.34902 0.15686]; 263 | lo = [0.57255 0.58431 0.56863]; 264 | hi = [0.84706 0.86275 0.83922]; 265 | 266 | % Plot 267 | 268 | delete(hs); 269 | hs = scatter(x(:),y(:),10,bin(:),'filled'); 270 | colormap([lo; cmap; hi]); 271 | set(gca, 'clim', [-0.5 length(clev)+0.5]); 272 | 273 | tklabel = ['lo' ... 274 | arrayfun(@(a,b) sprintf('%.1f - %.1f',a,b), ... 275 | clev(1:end-1), clev(2:end), 'uni', 0) ... 276 | 'hi']; 277 | set(hcb, 'ticks', 0:length(clev), 'ticklabels', tklabel); 278 | 279 | %% 280 | % We can try to use the same discretization trick to try to get our desired 281 | % filled contour plot. 282 | 283 | delete(hs); 284 | [cc, hc] = contourf(x,y,bin,0.5:1:length(clev)); 285 | 286 | %% 287 | % But that sacrifices the resolution of the contouring. We can also try 288 | % fiddle with the colormap, trying to get the colors to match our contour 289 | % levels. I do this via the 290 | % 291 | % function. 292 | 293 | loval = clev(1) - (clev(end)-clev(1))*0.1; 294 | hival = clev(end) + (clev(end)-clev(1))*0.1; 295 | 296 | ctable = [[loval clev]' [lo;cmap;hi]*255 [clev hival]' [lo;cmap;hi]*255]'; 297 | fid = fopen('cmaptemp.cpt', 'wt'); 298 | fprintf(fid, '%.2f %.0f %.0f %.0f %.2f %.0f %.0f %.0f\n', ctable); 299 | fclose(fid); 300 | [cmap2, lims] = cptcmap('cmaptemp'); 301 | delete('cmaptemp.cpt'); 302 | 303 | delete(hc); 304 | [cc, hc] = contourf(x,y,z,clev); 305 | set(gca, 'clim', lims); 306 | colormap(cmap2); 307 | set(hcb, 'Ticks', clev, 'TickLabelsMode', 'auto'); 308 | 309 | %% 310 | % That mostly works, with a few drawbacks. First, tweaking the colormap to 311 | % look like it has uneven color intervals requires some manaul calculation. 312 | % Even with cptcmap, you have to do some setup ahead of time. Also, if we 313 | % add the scatter plot on top, we'll see a few issues: 314 | 315 | hs = scatter(x(:), y(:), 10, z(:), 'filled'); 316 | set(hs, 'markeredgecolor', 'w'); 317 | 318 | %% 319 | % Most of the regions match the dots... except where there should be dark 320 | % gray contours, i.e. where the data is lower than the specified lowest 321 | % contour. Depending on your Matlab version, when you run this, the 322 | % enclosed circle on the right may or may not be shaded. In all Matlab 323 | % versions, the unenclosed areas (the bits that hit up against the wall of 324 | % the grid) are unshaded. 325 | % 326 | % So this is where the extra calculations in contourfcmap come in handy: 327 | 328 | delete(hs); 329 | delete(hc); 330 | delete(hcb); 331 | 332 | h = contourfcmap(x,y,z,clev,cmap, ... 333 | 'lo', lo, ... 334 | 'hi', hi, ... 335 | 'cbarloc', 'eastoutside', ... 336 | 'method', 'calccontour'); 337 | 338 | %% 339 | % Verdict: It's slower than a simple contour plot. But it colors the 340 | % patches properly, including the lower-than-lowest-contour regions. And 341 | % there's no need to do any tricky colormap calculations. If your 342 | % application needs extremely fast rendering, and the lower-than-lowest 343 | % thing isn't a problem for you, you might be better off using one of the 344 | % tricks above. Otherwise, contourfcmap should be the easier solution. 345 | 346 | %% Contributions 347 | % 348 | % Community contributions to this package are welcome! 349 | % 350 | % To report bugs, please submit 351 | % on GitHub and 352 | % include: 353 | % 354 | % * your operating system 355 | % * your version of Matlab and all relevant toolboxes (type |ver| at the Matlab command line to get this info) 356 | % * code/data to reproduce the error or buggy behavior, and the full text of any error messages received 357 | % 358 | % Please also feel free to submit enhancement requests, or to send pull 359 | % requests (via GitHub) for bug fixes or new features. 360 | % 361 | % I do monitor the MatlabCentral FileExchange entry for any issues raised 362 | % in the comments, but would prefer to track issues on GitHub. 363 | % 364 | 365 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # contourfcmap.m: filled contour plot with precise colormap 3 | 4 | [![View contourfcmap: filled contour plot with precise colormap on File Exchange](https://www.mathworks.com/matlabcentral/images/matlab-file-exchange.svg)](https://www.mathworks.com/matlabcentral/fileexchange/29638-contourfcmap-filled-contour-plot-with-precise-colormap) 5 | 6 | Author: Kelly Kearney 7 | 8 | 9 | This repository includes the code for the `contourfcmap.m` Matlab function, along with all dependent functions required to run it. 10 | 11 | 12 | This function creates a shaded contour map, similar to that created by the contourf function. However, the relationship between a contourf plot and its colormap (i.e. exactly which color corresponds to each contour interval), can often be confusing and inconsistent. This function instead allows the user to specify exactly which colors to use in each interval, and also to choose colors for regions that exceed the contour line limits. 13 | 14 | 15 | 16 | ## Contents 17 | 18 | 19 | - Getting started 20 | - Syntax 21 | - Description 22 | - Examples 23 | - The algorithms behind contourfcmap 24 | - Contributions 25 | 26 | ## Getting started 27 | 28 | 29 | **Prerequisites** 30 | 31 | 32 | This function requires Matlab R14 or later. 33 | 34 | 35 | **Downloading and installation** 36 | 37 | 38 | This code can be downloaded from [Github](https://github.com/kakearney/contourfcmap-pkg/) or the [MatlabCentral File Exchange](http://www.mathworks.com/matlabcentral/fileexchange/29638). The File Exchange entry is updated daily from the GitHub repository. 39 | 40 | 41 | **Matlab Search Path** 42 | 43 | 44 | The following folders need to be added to your Matlab Search path (via `addpath`, `pathtool`, etc.): 45 | 46 | 47 | 48 | ```matlab 49 | contourfcmap-pkg/FEX-function_handle 50 | contourfcmap-pkg/arclength 51 | contourfcmap-pkg/contourcs 52 | contourfcmap-pkg/contourfcmap 53 | contourfcmap-pkg/distance2curve 54 | contourfcmap-pkg/fillnan 55 | contourfcmap-pkg/interparc 56 | contourfcmap-pkg/minmax 57 | contourfcmap-pkg/multiplepolyint 58 | contourfcmap-pkg/parsepv 59 | contourfcmap-pkg/pcolorbar 60 | ``` 61 | 62 | 63 | 64 | ## Syntax 65 | 66 | 67 | 68 | ```matlab 69 | contourfcmap(x,y,z,clev,cmap) 70 | contourfcmap(x,y,z,clev,cmap, Name, Value) 71 | h = contourfcmap(...) 72 | ``` 73 | 74 | 75 | 76 | ## Description 77 | 78 | 79 | `contourfcmap(x,y,z,clev,cmap)` plots a filled contour plot of the matrix `z` with coordinates `x` and `y`. `z` must be at least a 2x2 matrix; `x` and `y` can either be matrices defining the grid for `z`, or vectors that correspond to the column and row coordinates, respectively. `clev` is an n x 1 vector defining the contour line levels, and `cmap` is an n-1 x 1 colormap array defining the colors to be used between each of the contour levels. 80 | 81 | 82 | `contourfcmap(x,y,z,clev,cmap, 'lo', lo)` indicates the color value `lo` (a 1 x 3 RGB array) to be used for any region with data lower than the first contour level. Default is white. 83 | 84 | 85 | `contourfcmap(x,y,z,clev,cmap, 'hi', hi)` indicates the color value `hi` (a 1 x 3 RGB array) to be used for any region with data higher than the last contour level. Default is white. 86 | 87 | 88 | `contourfcmap(x,y,z,clev,cmap, 'method', method)` allows you to switch between the 'recolor' and 'calccontour' algorithms (see below for details). Default is 'recolor' (though I now recommend 'calccontour' for R2017b or later). 89 | 90 | 91 | `contourfcmap(x,y,z,clev,cmap, 'cbarloc', cbarloc)` adds a psuedo-colorbar using the colorbar location indicator `cbarloc`. Default is no colorbar. 92 | 93 | 94 | `contourfcmap(x,y,z,clev,cmap, 'evencb', evencb)` indicates whether to space the colors on the colorbar evenly (evencb = true) or to size them according to the clev values (evencb = false). Default is false. 95 | 96 | 97 | `h = contourfcmap(...)` returns a structure `h` whose fields hold the colorbar axis handle structure (`h.cb`, see `pcolorbar.m` for details), contour object (`h.h`), contour matrix (`h.c`), and patch object (`h.p`). 98 | 99 | 100 | 101 | ## Examples 102 | 103 | 104 | First we'll plot a contourf plot using the standard Matlab functions. Without labeling the contour lines, it's difficult to tell which values in the colorbar correspond to the color intervals in the contourf plot. 105 | 106 | 107 | 108 | ```matlab 109 | count = 0; 110 | 111 | % Data 112 | 113 | [x,y] = meshgrid(linspace(0,1,100)); 114 | z = peaks(100); 115 | clev = [-5 -3 -2:.5:2 3 5]; 116 | 117 | % Set up axes 118 | 119 | h.fig = figure('color', 'w'); 120 | h.ax(1) = axes('position', [0.05 0.25 0.425 0.5]); 121 | h.ax(2) = axes('position', [0.525 0.25 0.425 0.5]); 122 | 123 | % Plot 124 | 125 | axes(h.ax(1)); 126 | contourf(x,y,z,clev); 127 | cb = colorbar('eastoutside'); 128 | colormap(h.ax(1), jet); 129 | title(h.ax(1), 'contourf'); 130 | ``` 131 | 132 | 133 | ![](./readmeExtras/README_01.png) 134 | 135 | Using contourfcmap, we can set the colors explictly, so it's much easier to tell exactly which color corresponds to which value. 136 | 137 | 138 | 139 | ```matlab 140 | axes(h.ax(2)); 141 | hc = contourfcmap(x,y,z,clev,jet(12), ... 142 | 'lo', [.8 .8 .8], ... 143 | 'hi', [.2 .2 .2], ... 144 | 'cbarloc', 'eastoutside', ... 145 | 'method', 'calccontour'); 146 | title(h.ax(2), 'contourfcmap'); 147 | ``` 148 | 149 | 150 | ![](./readmeExtras/README_02.png) 151 | 152 | If you prefer, you can set the colorbar to show the contour intervals evenly-spaced, even if the values aren't. 153 | 154 | 155 | 156 | ```matlab 157 | delete(hc.cb.cb); 158 | delete(hc.cb.ax); 159 | cla; 160 | 161 | hc = contourfcmap(x,y,z,clev,jet(12), ... 162 | 'lo', [.8 .8 .8], ... 163 | 'hi', [.2 .2 .2], ... 164 | 'cbarloc', 'eastoutside', ... 165 | 'method', 'calccontour', ... 166 | 'evencb', true); 167 | title(h.ax(2), 'contourfcmap'); 168 | ``` 169 | 170 | 171 | ![](./readmeExtras/README_03.png) 172 | 173 | 174 | ## The algorithms behind contourfcmap 175 | 176 | 177 | This function began its life as a very simple function back in 2010. At that time, it was just a wrapper around `contourf`. A contour object in Maltab circa 2010 was a combination of some lines (the contour lines) and some patches (the shaded regions), and my function just looped over those patches and assigned new colors to their CData properties. 178 | 179 | 180 | Then the 2014b release came along, and with it, a complete overhaul of Matlab's graphics objects. Under these new second-generation handle graphics (i.e. HG2), contour objects became more complex. They now were composed of TriangleStrips, a low-level, mostly-undocumented graphics object that really wasn't designed to be tampered with. 181 | 182 | 183 | I updated my function to repeat its old processes, looping over the contours and changing the colors. But those color changes weren't very permanent. Changing the colormap, adding a colorbar, and most frustratingly, printing figures to file (using anything that relies on the print command, including print, saveas, export_fig, etc) undoes it. My function was pretty useless if I couldn't get changes to stick. 184 | 185 | 186 | So in 2014, I introduced a new algorithm to contourfcmap, the 'calccontour' method. This version still uses the contour function to do the heavy lifting of the contouring calculations (contouring is hard!), but then tries to draw the shaded region using patches, as in the old HG1 version. I originally considered this just a hacky stand-in until I could get recoloring to stick. It worked pretty well for simple datasets, but fell apart for more complicated ones, especially those involving NaNs and non-cartesian grids. But the recoloring option reset issue has persisted through newer releases of Matlab (actually, it's gotten worse). 187 | 188 | 189 | I've now (March 2018) just completed an overhaul of the calccontour option. This update may break back-compatibility with older code that uses contourfcmap, since I've modified some of the output variables (though it should still support all the older syntax in terms of input variables). I've expanded the code to be much more robust to non-cartesian grids and NaNs, and with the introduction of polyshape objects, the polygon calculations no longer require the Mapping Toolbox. Though still a bit slow to plot, this algorithm also fixes a few issues with the way Matlab colors (or rather, doesn't color) lower-than-lowest-contour regions. And finally, I've altered the colorbar code to rely on my pcolorbar function, which provides for more robust resizing (similar to a real colorbar) when one makes changes to the axis. 190 | 191 | 192 | This example walks you through my thought process with this algorithm, and some of the issues with contourf that I'm trying to address. 193 | 194 | 195 | We'll start with the peaks data: 196 | 197 | 198 | 199 | ```matlab 200 | close all; 201 | figure; 202 | 203 | [x,y,z] = peaks; 204 | 205 | hs = scatter(x(:),y(:),10,z(:),'filled'); 206 | hcb = colorbar; 207 | set(gca, 'clim', [-15 15], 'xlim', [-4.5 4.5], 'ylim', [-4.5 4.5]); 208 | hold on; 209 | ``` 210 | 211 | 212 | ![](./readmeExtras/README_04.png) 213 | 214 | Most of Matlab's example contour data assumes a cartesian grid (i.e. data aligned along the x- and y-axes, like an image). But it doesn't need to be... any structured grid will work. Let's add some skew and rotation, to make sure this example covers the more complicated cases: 215 | 216 | 217 | 218 | ```matlab 219 | y = y + sin(x); 220 | th = pi/3; 221 | R = [cos(th) -sin(th); sin(th) cos(th)]; 222 | xy = R * [x(:) y(:)]'; 223 | x = reshape(xy(1,:), size(x)); 224 | y = reshape(xy(2,:), size(y)); 225 | 226 | delete(hs); 227 | hs = scatter(x(:),y(:),10,z(:),'filled'); 228 | ``` 229 | 230 | 231 | ![](./readmeExtras/README_05.png) 232 | 233 | Contour data can also include NaNs. Sometimes these represent a missing data point or two. But they can also occur in bigger blocks; for example, a map of ocean data might represent land with NaNs. We'll add both enclosed (surrounded by data) and unenclosed (connecting to side of grid) NaNs, and make sure we have some extra data islands of high and low data in there too (you'll see why in a bit): 234 | 235 | 236 | 237 | ```matlab 238 | z(z < 0.2 & z > 0) = NaN; 239 | z(1:3,1:3) = -15; 240 | z(end-3:end,end-3:end) = 15; 241 | z(25:30, 1:2) = -15; 242 | 243 | delete(hs); 244 | hs = scatter(x(:),y(:),10,z(:),'filled'); 245 | ``` 246 | 247 | 248 | ![](./readmeExtras/README_06.png) 249 | 250 | Let's say we want to plot some contours, with specific colors between each interval. We can show this by plotting a scatter plot of the discretized values: 251 | 252 | 253 | 254 | ```matlab 255 | % Discretize 256 | 257 | bin = discretize(z, clev); 258 | bin(z < clev(1)) = 0; 259 | bin(z > clev(end)) = length(clev)+1; 260 | 261 | % Colors 262 | 263 | clev = [-5 -3 -2:.5:2 3 5]; 264 | cmap = [... 265 | 0.65098 0.80784 0.8902 266 | 0.12157 0.47059 0.70588 267 | 0.69804 0.87451 0.54118 268 | 0.2 0.62745 0.17255 269 | 0.98431 0.60392 0.6 270 | 0.8902 0.10196 0.1098 271 | 0.99216 0.74902 0.43529 272 | 1 0.49804 0 273 | 0.79216 0.69804 0.83922 274 | 0.41569 0.23922 0.60392 275 | 1 1 0.6 276 | 0.69412 0.34902 0.15686]; 277 | lo = [0.57255 0.58431 0.56863]; 278 | hi = [0.84706 0.86275 0.83922]; 279 | 280 | % Plot 281 | 282 | delete(hs); 283 | hs = scatter(x(:),y(:),10,bin(:),'filled'); 284 | colormap([lo; cmap; hi]); 285 | set(gca, 'clim', [-0.5 length(clev)+0.5]); 286 | 287 | tklabel = ['lo' ... 288 | arrayfun(@(a,b) sprintf('%.1f - %.1f',a,b), ... 289 | clev(1:end-1), clev(2:end), 'uni', 0) ... 290 | 'hi']; 291 | set(hcb, 'ticks', 0:length(clev), 'ticklabels', tklabel); 292 | ``` 293 | 294 | 295 | ![](./readmeExtras/README_07.png) 296 | 297 | We can try to use the same discretization trick to try to get our desired filled contour plot. 298 | 299 | 300 | 301 | ```matlab 302 | delete(hs); 303 | [cc, hc] = contourf(x,y,bin,0.5:1:length(clev)); 304 | ``` 305 | 306 | 307 | ![](./readmeExtras/README_08.png) 308 | 309 | But that sacrifices the resolution of the contouring. We can also try fiddle with the colormap, trying to get the colors to match our contour levels. I do this via the [cptcmap](https://www.mathworks.com/matlabcentral/fileexchange/28943-color-palette-tables---cpt--for-matlab) function. 310 | 311 | 312 | 313 | ```matlab 314 | loval = clev(1) - (clev(end)-clev(1))*0.1; 315 | hival = clev(end) + (clev(end)-clev(1))*0.1; 316 | 317 | ctable = [[loval clev]' [lo;cmap;hi]*255 [clev hival]' [lo;cmap;hi]*255]'; 318 | fid = fopen('cmaptemp.cpt', 'wt'); 319 | fprintf(fid, '%.2f %.0f %.0f %.0f %.2f %.0f %.0f %.0f\n', ctable); 320 | fclose(fid); 321 | [cmap2, lims] = cptcmap('cmaptemp'); 322 | delete('cmaptemp.cpt'); 323 | 324 | delete(hc); 325 | [cc, hc] = contourf(x,y,z,clev); 326 | set(gca, 'clim', lims); 327 | colormap(cmap2); 328 | set(hcb, 'Ticks', clev, 'TickLabelsMode', 'auto'); 329 | ``` 330 | 331 | 332 | ![](./readmeExtras/README_09.png) 333 | 334 | That mostly works, with a few drawbacks. First, tweaking the colormap to look like it has uneven color intervals requires some manaul calculation. Even with cptcmap, you have to do some setup ahead of time. Also, if we add the scatter plot on top, we'll see a few issues: 335 | 336 | 337 | 338 | ```matlab 339 | hs = scatter(x(:), y(:), 10, z(:), 'filled'); 340 | set(hs, 'markeredgecolor', 'w'); 341 | ``` 342 | 343 | 344 | ![](./readmeExtras/README_10.png) 345 | 346 | Most of the regions match the dots... except where there should be dark gray contours, i.e. where the data is lower than the specified lowest contour. Depending on your Matlab version, when you run this, the enclosed circle on the right may or may not be shaded. In all Matlab versions, the unenclosed areas (the bits that hit up against the wall of the grid) are unshaded. 347 | 348 | 349 | So this is where the extra calculations in contourfcmap come in handy: 350 | 351 | 352 | 353 | ```matlab 354 | delete(hs); 355 | delete(hc); 356 | delete(hcb); 357 | 358 | h = contourfcmap(x,y,z,clev,cmap, ... 359 | 'lo', lo, ... 360 | 'hi', hi, ... 361 | 'cbarloc', 'eastoutside', ... 362 | 'method', 'calccontour'); 363 | ``` 364 | 365 | 366 | ![](./readmeExtras/README_11.png) 367 | 368 | Verdict: It's slower than a simple contour plot. But it colors the patches properly, including the lower-than-lowest-contour regions. And there's no need to do any tricky colormap calculations. If your application needs extremely fast rendering, and the lower-than-lowest thing isn't a problem for you, you might be better off using one of the tricks above. Otherwise, contourfcmap should be the easier solution. 369 | 370 | 371 | 372 | ## Contributions 373 | 374 | 375 | Community contributions to this package are welcome! 376 | 377 | 378 | To report bugs, please submit [an issue](https://github.com/kakearney/contourfcmap-pkg/issues) on GitHub and include: 379 | 380 | 381 | 382 | - your operating system 383 | - your version of Matlab and all relevant toolboxes (type `ver` at the Matlab command line to get this info) 384 | - code/data to reproduce the error or buggy behavior, and the full text of any error messages received 385 | 386 | Please also feel free to submit enhancement requests, or to send pull requests (via GitHub) for bug fixes or new features. 387 | 388 | 389 | I do monitor the MatlabCentral FileExchange entry for any issues raised in the comments, but would prefer to track issues on GitHub. 390 | 391 | 392 | 393 | [Published with MATLAB R2018a]("http://www.mathworks.com/products/matlab/") 394 | -------------------------------------------------------------------------------- /arclength/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | ehthumbs.db 37 | Thumbs.db -------------------------------------------------------------------------------- /arclength/arclength.m: -------------------------------------------------------------------------------- 1 | function [arclen,seglen] = arclength(px,py,varargin) 2 | % arclength: compute arc length of a space curve, or any curve represented as a sequence of points 3 | % usage: [arclen,seglen] = arclength(px,py) % a 2-d curve 4 | % usage: [arclen,seglen] = arclength(px,py,pz) % a 3-d space curve 5 | % usage: [arclen,seglen] = arclength(px,py,method) % specifies the method used 6 | % 7 | % Computes the arc length of a function or any 8 | % general 2-d, 3-d or higher dimensional space 9 | % curve using various methods. 10 | % 11 | % arguments: (input) 12 | % px, py, pz, ... - vectors of length n, defining points 13 | % along the curve. n must be at least 2. Replicate 14 | % points should not be present in the curve. 15 | % 16 | % method - (OPTIONAL) string flag - denotes the method 17 | % used to compute the arc length of the curve. 18 | % 19 | % method may be any of 'linear', 'spline', or 'pchip', 20 | % or any simple contraction thereof, such as 'lin', 21 | % 'sp', or even 'p'. 22 | % 23 | % method == 'linear' --> Uses a linear chordal 24 | % approximation to compute the arc length. 25 | % This method is the most efficient. 26 | % 27 | % method == 'pchip' --> Fits a parametric pchip 28 | % approximation, then integrates the 29 | % segments numerically. 30 | % 31 | % method == 'spline' --> Uses a parametric spline 32 | % approximation to fit the curves, then 33 | % integrates the segments numerically. 34 | % Generally for a smooth curve, this 35 | % method may be most accurate. 36 | % 37 | % DEFAULT: 'linear' 38 | % 39 | % 40 | % arguments: (output) 41 | % arclen - scalar total arclength of all curve segments 42 | % 43 | % seglen - arclength of each independent curve segment 44 | % there will be n-1 segments for which the 45 | % arc length will be computed. 46 | % 47 | % 48 | % Example: 49 | % % Compute the length of the perimeter of a unit circle 50 | % theta = linspace(0,2*pi,10); 51 | % x = cos(theta); 52 | % y = sin(theta); 53 | % 54 | % % The exact value is 55 | % 2*pi 56 | % % ans = 57 | % % 6.28318530717959 58 | % 59 | % % linear chord lengths 60 | % arclen = arclength(x,y,'l') 61 | % % arclen = 62 | % % 6.1564 63 | % 64 | % % Integrated pchip curve fit 65 | % arclen = arclength(x,y,'p') 66 | % % arclen = 67 | % % 6.2782 68 | % 69 | % % Integrated spline fit 70 | % arclen = arclength(x,y,'s') 71 | % % arclen = 72 | % % 6.2856 73 | % 74 | % Example: 75 | % % A (linear) space curve in 5 dimensions 76 | % x = 0:.25:1; 77 | % y = x; 78 | % z = x; 79 | % u = x; 80 | % v = x; 81 | % 82 | % % The length of this curve is simply sqrt(5) 83 | % % since the "curve" is merely the diagonal of a 84 | % % unit 5 dimensional hyper-cube. 85 | % [arclen,seglen] = arclength(x,y,z,u,v,'l') 86 | % % arclen = 87 | % % 2.23606797749979 88 | % % seglen = 89 | % % 0.559016994374947 90 | % % 0.559016994374947 91 | % % 0.559016994374947 92 | % % 0.559016994374947 93 | % 94 | % 95 | % See also: interparc, spline, pchip, interp1 96 | % 97 | % Author: John D'Errico 98 | % e-mail: woodchips@rochester.rr.com 99 | % Release: 1.0 100 | % Release date: 3/10/2010 101 | 102 | % unpack the arguments and check for errors 103 | if nargin < 2 104 | error('ARCLENGTH:insufficientarguments', ... 105 | 'at least px and py must be supplied') 106 | end 107 | 108 | n = length(px); 109 | % are px and py both vectors of the same length? 110 | if ~isvector(px) || ~isvector(py) || (length(py) ~= n) 111 | error('ARCLENGTH:improperpxorpy', ... 112 | 'px and py must be vectors of the same length') 113 | elseif n < 2 114 | error('ARCLENGTH:improperpxorpy', ... 115 | 'px and py must be vectors of length at least 2') 116 | end 117 | 118 | % compile the curve into one array 119 | data = [px(:),py(:)]; 120 | 121 | % defaults for method and tol 122 | method = 'linear'; 123 | 124 | % which other arguments are included in varargin? 125 | if numel(varargin) > 0 126 | % at least one other argument was supplied 127 | for i = 1:numel(varargin) 128 | arg = varargin{i}; 129 | if ischar(arg) 130 | % it must be the method 131 | validmethods = {'linear' 'pchip' 'spline'}; 132 | ind = strmatch(lower(arg),validmethods); 133 | if isempty(ind) || (length(ind) > 1) 134 | error('ARCLENGTH:invalidmethod', ... 135 | 'Invalid method indicated. Only ''linear'',''pchip'',''spline'' allowed.') 136 | end 137 | method = validmethods{ind}; 138 | 139 | else 140 | % it must be pz, defining a space curve in higher dimensions 141 | if numel(arg) ~= n 142 | error('ARCLENGTH:inconsistentpz', ... 143 | 'pz was supplied, but is inconsistent in size with px and py') 144 | end 145 | 146 | % expand the data array to be a 3-d space curve 147 | data = [data,arg(:)]; %#ok 148 | end 149 | end 150 | 151 | end 152 | 153 | % what dimension do we live in? 154 | nd = size(data,2); 155 | 156 | % compute the chordal linear arclengths 157 | seglen = sqrt(sum(diff(data,[],1).^2,2)); 158 | arclen = sum(seglen); 159 | 160 | % we can quit if the method was 'linear'. 161 | if strcmpi(method,'linear') 162 | % we are now done. just exit 163 | return 164 | end 165 | 166 | % 'spline' or 'pchip' must have been indicated, 167 | % so we will be doing an integration. Save the 168 | % linear chord lengths for later use. 169 | chordlen = seglen; 170 | 171 | % compute the splines 172 | spl = cell(1,nd); 173 | spld = spl; 174 | diffarray = [3 0 0;0 2 0;0 0 1;0 0 0]; 175 | for i = 1:nd 176 | switch method 177 | case 'pchip' 178 | spl{i} = pchip([0;cumsum(chordlen)],data(:,i)); 179 | case 'spline' 180 | spl{i} = spline([0;cumsum(chordlen)],data(:,i)); 181 | nc = numel(spl{i}.coefs); 182 | if nc < 4 183 | % just pretend it has cubic segments 184 | spl{i}.coefs = [zeros(1,4-nc),spl{i}.coefs]; 185 | spl{i}.order = 4; 186 | end 187 | end 188 | 189 | % and now differentiate them 190 | xp = spl{i}; 191 | xp.coefs = xp.coefs*diffarray; 192 | xp.order = 3; 193 | spld{i} = xp; 194 | end 195 | 196 | % numerical integration along the curve 197 | polyarray = zeros(nd,3); 198 | for i = 1:spl{1}.pieces 199 | % extract polynomials for the derivatives 200 | for j = 1:nd 201 | polyarray(j,:) = spld{j}.coefs(i,:); 202 | end 203 | 204 | % integrate the arclength for the i'th segment 205 | % using quadgk for the integral. I could have 206 | % done this part with an ode solver too. 207 | seglen(i) = quadgk(@(t) segkernel(t),0,chordlen(i)); 208 | end 209 | 210 | % and sum the segments 211 | arclen = sum(seglen); 212 | 213 | % ========================== 214 | % end main function 215 | % ========================== 216 | % begin nested functions 217 | % ========================== 218 | function val = segkernel(t) 219 | % sqrt((dx/dt)^2 + (dy/dt)^2) 220 | 221 | val = zeros(size(t)); 222 | for k = 1:nd 223 | val = val + polyval(polyarray(k,:),t).^2; 224 | end 225 | val = sqrt(val); 226 | 227 | end % function segkernel 228 | 229 | end % function arclength 230 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /arclength/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, John D'Errico 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /contourcs/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | ehthumbs.db 37 | Thumbs.db -------------------------------------------------------------------------------- /contourcs/contourcs.m: -------------------------------------------------------------------------------- 1 | function Cout = contourcs(varargin) 2 | %CONTOURCS Wrapper to CONTOURS to Obtain Structure Output 3 | % S = CONTOURCS(...) takes the exact same input arguments as the default 4 | % CONTOURC but its output is a struct array with fields: 5 | % 6 | % Level - contour line value 7 | % Length - number of contour line points 8 | % X - X coordinate array of the contour line 9 | % Y - Y coordinate array of the contour line 10 | % 11 | % See also contourc. 12 | 13 | % Version 1.0 (Aug 11, 2010) 14 | % Written by: Takeshi Ikuma 15 | % Revision History: 16 | % - (Aug. 11, 2010) : initial release 17 | 18 | % Modification by K.Kearney: Allow either contourc-style input, or a 19 | % pre-generation contour matrix. Indicate the latter with the string 20 | % 'cmat' as a second input. 21 | 22 | if nargin == 2 && strcmp(varargin{2}, 'cmat') 23 | C = varargin{1}; 24 | else 25 | % Run CONTOURC and get output matrix 26 | try 27 | C = contourc(varargin{:}); 28 | catch ME 29 | throwAsCaller(ME); 30 | end 31 | end 32 | 33 | % Run CONTOURC and get output matrix 34 | % try 35 | % C = contourc(varargin{:}); 36 | % catch ME 37 | % throwAsCaller(ME); 38 | % end 39 | 40 | % Count number of contour segments found (K) 41 | K = 0; 42 | n0 = 1; 43 | while n0<=size(C,2) 44 | K = K + 1; 45 | n0 = n0 + C(2,n0) + 1; 46 | end 47 | 48 | % initialize output struct 49 | el = cell(K,1); 50 | Cout = struct('Level',el,'Length',el,'X',el,'Y',el); 51 | 52 | % fill the output struct 53 | n0 = 1; 54 | for k = 1:K 55 | Cout(k).Level = C(1,n0); 56 | idx = (n0+1):(n0+C(2,n0)); 57 | Cout(k).Length = C(2,n0); 58 | Cout(k).X = C(1,idx); 59 | Cout(k).Y = C(2,idx); 60 | n0 = idx(end) + 1; % next starting index 61 | end 62 | 63 | % Copyright (c)2010, Takeshi Ikuma 64 | % All rights reserved. 65 | % 66 | % Redistribution and use in source and binary forms, with or without 67 | % modification, are permitted provided that the following conditions are 68 | % met: 69 | % 70 | % * Redistributions of source code must retain the above copyright 71 | % notice, this list of conditions and the following disclaimer. 72 | % * Redistributions in binary form must reproduce the above copyright 73 | % notice, this list of conditions and the following disclaimer in the 74 | % documentation and/or other materials provided with the distribution. 75 | % * Neither the names of its contributors may be used to endorse or 76 | % promote products derived from this software without specific prior 77 | % written permission. 78 | % 79 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 80 | % IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 81 | % THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 82 | % PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 83 | % CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 84 | % EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 85 | % PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 86 | % PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 87 | % LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 88 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 89 | % SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 90 | -------------------------------------------------------------------------------- /contourcs/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Kesh Ikuma 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /contourfcmap/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | ehthumbs.db 37 | Thumbs.db -------------------------------------------------------------------------------- /contourfcmap/contourfcmap.m: -------------------------------------------------------------------------------- 1 | function varargout = contourfcmap(x,y,z,clev,cmap,varargin) % lo,hi,cbarloc,evencb) 2 | %CONTOURFCMAP Filled contour plot with specified colors 3 | % 4 | % h = contourfcmap(x,y,z,clev,cmap,lo,hi,cbarloc) 5 | % h = contourfcmap(x,y,z,clev,cmap) 6 | % h = contourfcmap(x,y,z,clev,cmap,lo) 7 | % h = contourfcmap(x,y,z,clev,cmap,lo,hi) 8 | % h = contourfcmap(x,y,z,clev,cmap, param1, val1, ...) 9 | % 10 | % This function creates a shaded contour map, similar to that created by 11 | % the contourf function. However, the relationship between a contourf plot 12 | % and its colormap (i.e. exactly which color corresponds to each contour 13 | % interval), can often be confusing and inconsistent, in my opinion. This 14 | % function instead allows the user to specify exactly which colors to use 15 | % in each interval, and also to choose colors for regions that exceed the 16 | % contour line limits. 17 | % 18 | % Input variables 19 | % 20 | % x: x-coordinates of grid, following size restrictions of surf 21 | % command 22 | % 23 | % y: y-coordinates of grid, following size restrictions of surf 24 | % command 25 | % 26 | % z: array of data to be contoured, following size restritions 27 | % of surf command 28 | % 29 | % clev: vector of length n, contour levels, must be monotonically 30 | % increasing 31 | % 32 | % cmap: n-1 x 3 colormap array, specifying colors used to shade 33 | % each contour interval 34 | % 35 | % Optional input variables (passed as parameter/value pairs) 36 | % 37 | % lo: 1 x 3 colormap array, specifying color used for all data 38 | % falling below the first contour level. If not included or 39 | % empty, default will be to white. 40 | % 41 | % hi: 1 x 3 colormap array, specifying color used for all data 42 | % falling above the last contour level. If not included or 43 | % empty, default will be to white. 44 | % 45 | % cbarloc: string specifying colorbar location (see colorbar), or a 46 | % 1 x 4 position vector for colorbar. If not included, no 47 | % colorbar will be created. Note that the colorbar created 48 | % is not a colorbar object, but just an axis with plotted 49 | % patches; it is for labeling only and is not linked to the 50 | % "peer axis" in any way. Default is no colorbar. 51 | % 52 | % evencb: logical scalar. If true, intervals on the colorbar will 53 | % be evenly spaced, regardless of value. If false, ticks 54 | % will correspond to clev values. If not included or empty, 55 | % default is false. 56 | % 57 | % method: string specifying calculation method: 58 | % 59 | % 'recolor': the default, create a contourf object, and 60 | % then change the color properties of the 61 | % underlying components. Note: In 2014b, 62 | % recolor will not persist when saving to 63 | % file via anything that uses print.m 64 | % (including print, saveas, export_fig, etc) 65 | % 66 | % 'calccontour': Create new patch and line objects from 67 | % scratch. This method requires the Mapping 68 | % Toolbox. It can be beneficial if you want 69 | % more consistency between output regardless 70 | % of which version of Matlab is being run. 71 | % It also properly fills regions falling 72 | % below the lowest contour level and above 73 | % the highest contour level, which isn't 74 | % always the case with contourf-generated 75 | % contour objects. 76 | % 77 | % Output variables: 78 | % 79 | % h: 1 x 1 structure, with the following fields (some may not be 80 | % present, depending on input options) 81 | % 82 | % c: contour matrix for contour lines 83 | % 84 | % h: handle to contourgroup or contour object (for 85 | % 'recolor' method, contour faces indicate color; for 86 | % 'calccontour' method, contour faces are left 87 | % uncolored). 88 | % 89 | % p: handles to patch objects (the filled contour 90 | % regions) ('calccontour' method only) 91 | % 92 | % cb: structure of colorbar-related handles (see 93 | % pcolorbar.m) 94 | % 95 | % Example: 96 | % 97 | % [x,y] = meshgrid(linspace(0,1,100)); 98 | % z = peaks(100); 99 | % contourfcmap(x,y,z,[-5 -3 -2:.5:2 3 5],jet(12), ... 100 | % [.8 .8 .8], [.2 .2 .2], 'eastoutside') 101 | % 102 | 103 | % Copyright 2010-2016 Kelly Kearney 104 | 105 | %------------------------ 106 | % Parse input 107 | %------------------------ 108 | 109 | % New syntax uses parameter/value options, but we'll still accept the old 110 | % syntax 111 | 112 | isc = cellfun(@ischar, varargin); 113 | if ~mod(length(varargin),2) && all(isc(1:2:end)) 114 | 115 | if verLessThan('matlab', '8.2.0') % R2013b 116 | pvfun = 'addParamValue'; 117 | else 118 | pvfun = 'addParameter'; 119 | end 120 | 121 | % New syntax 122 | 123 | p = inputParser; 124 | p.(pvfun)('lo', [1 1 1], @(x) validateattributes(x, {'numeric'}, {'size', [1 3], '<=', 1, '>=', 0})); 125 | p.(pvfun)('hi', [1 1 1], @(x) validateattributes(x, {'numeric'}, {'size', [1 3], '<=', 1, '>=', 0})); 126 | p.(pvfun)('cbarloc', []); 127 | p.(pvfun)('evencb', false, @(x) validateattributes(x, {'logical'}, {'scalar'})); 128 | p.(pvfun)('method', 'recolor', @(x) validateattributes(x, {'char','string'}, {'scalartext'})); 129 | 130 | p.parse(varargin{:}); 131 | Opt = p.Results; 132 | 133 | validatestring(Opt.method, {'recolor', 'calccontour'}, 'contourfcmap', 'method'); 134 | 135 | % Opt = parsepv(Opt, varargin); 136 | 137 | else 138 | % Old syntax 139 | 140 | Opt.lo = [1 1 1]; 141 | Opt.hi = [1 1 1]; 142 | Opt.cbarloc = []; 143 | Opt.evencb = false; 144 | Opt.method = 'recolor'; 145 | Opt.flag = true; 146 | 147 | if verLessThan('matlab', 'R2011b') 148 | error(nargchk(5,9,nargin)); 149 | else 150 | narginchk(5,9); 151 | end 152 | 153 | fld = fieldnames(Opt); 154 | for ii = 1:length(varargin) 155 | Opt.(fld{ii}) = varargin{ii}; 156 | end 157 | end 158 | 159 | % Check version and dependencies 160 | 161 | useps = false; 162 | if strcmp(Opt.method, 'calccontour') 163 | if verLessThan('matlab', '9.3.0') % R2017b 164 | vmap = ver('map'); 165 | if isempty(vmap) 166 | error('The calccontour method requires either Matlab R2017b or later, or the Mapping Toolbox'); 167 | end 168 | else 169 | useps = true; 170 | W = warning('off', 'MATLAB:polyshape:repairedBySimplify'); 171 | end 172 | end 173 | 174 | % Check clev 175 | 176 | if ~isvector(clev) 177 | error('clev must be a vector'); 178 | end 179 | nlev = length(clev); 180 | clev = reshape(clev, 1, []); 181 | 182 | % Check cmap 183 | 184 | if size(cmap,2) ~=3 || size(cmap,1) ~= (nlev-1) || any(cmap(:)<0|cmap(:)>1) 185 | error('cmap must be nlev-1 x 3 colormap array'); 186 | end 187 | 188 | % Colors for too-low and too-high patches 189 | 190 | if isempty(Opt.lo) 191 | Opt.lo = [1 1 1]; 192 | end 193 | 194 | if isempty(Opt.hi) 195 | Opt.hi = [1 1 1]; 196 | end 197 | 198 | if ~isequal(size(Opt.lo),[1 3]) 199 | error('Opt.lo must be 1 x 3 colormap array'); 200 | end 201 | 202 | if ~isequal(size(Opt.hi),[1 3]) 203 | error('Opt.hi must be 1 x 3 colormap array'); 204 | end 205 | 206 | % Colorbar location 207 | 208 | if ~isempty(Opt.cbarloc) 209 | pos = {'north', 'south', 'east', 'west', 'northoutside', 'southoutside', 'eastoutside', 'westoutside'}; 210 | if ischar(Opt.cbarloc) 211 | if ~any(strcmp(lower(Opt.cbarloc), pos)) 212 | error('Unrecognized colorbar position'); 213 | end 214 | elseif ~isequal(size(Opt.cbarloc), [1 4]) || any(Opt.cbarloc > 1 | Opt.cbarloc < 0) 215 | error('cbarloc must be position string or 1 x 4 normalized position'); 216 | end 217 | showcb = true; 218 | else 219 | showcb = false; 220 | end 221 | 222 | if nargin < 9 || isempty(Opt.cbarloc) 223 | Opt.evencb = false; 224 | end 225 | 226 | % Axis 227 | 228 | ax = gca; 229 | 230 | %------------------------ 231 | % X/Y pattern 232 | %------------------------ 233 | 234 | % X and Y can either be vectors, or matrices, and the matrices can be 235 | % irregular. Here I check whether the input coordinate arrays are vectors, 236 | % meshgrid-style matrices, ndgrid-style matrices, or an irregular (but 237 | % still structured!) grid. 238 | 239 | isvec = isvector(x) && ... 240 | isvector(y) && ... 241 | ((length(x)==size(z,2) && ... 242 | length(y)==size(z,1)) || ... 243 | (length(x)==size(z,1) && ... 244 | length(y)==size(z,2))); 245 | 246 | ismgrid = isequal(size(x),size(y),size(z)) && ... 247 | ~any(reshape(bsxfun(@minus, x, x(1,:)),[],1)) && ... 248 | ~any(reshape(bsxfun(@minus, y, y(:,1)),[],1)); 249 | isngrid = isequal(size(x),size(y),size(z)) && ... 250 | ~any(reshape(bsxfun(@minus, x, x(:,1)),[],1)) && ... 251 | ~any(reshape(bsxfun(@minus, y, y(1,:)),[],1)); 252 | 253 | isigrid = isequal(size(x),size(y),size(z)) && ... 254 | ~ismgrid && ... 255 | ~isngrid; 256 | 257 | if ~(isvec || ismgrid || isngrid || isigrid) 258 | htmp = contourf(x,y,z); 259 | 260 | % If this works, it means I've found an input format I hadn't 261 | % considered in my code. Might work, might not. Email me if you hit 262 | % this. 263 | 264 | warning('You''ve found an accepatable x/y/z format that I haven''t tested... may have unexpected results'); 265 | delete(htmp); 266 | end 267 | 268 | % Convert all rectilinear input format to vector x and y, with z rows 269 | % corresponding to y and columns to x. Irregular grids will stay as they 270 | % are. 271 | 272 | if isigrid 273 | irrflag = true; % Flag for irregular grid 274 | else 275 | irrflag = false; 276 | if isvec 277 | if size(z,2) ~= length(x) 278 | z = z'; 279 | end 280 | elseif ismgrid 281 | x = x(1,:); 282 | y = y(:,1); 283 | elseif isngrid 284 | x = x(:,1); 285 | y = y(1,:); 286 | z = z'; 287 | end 288 | end 289 | 290 | %------------------------ 291 | % Create colorbar 292 | %------------------------ 293 | 294 | % Need to create the colorbar up front, otherwise doing so will mess up 295 | % recoloring 296 | 297 | if showcb 298 | 299 | dy = 0.05 .* (max(clev) - min(clev)); 300 | clevcbar = [clev(1)-dy; clev(:); clev(end)+dy]; 301 | cmapcbar = [Opt.lo; cmap; Opt.hi]; 302 | 303 | if ~ischar(Opt.cbarloc) 304 | cbarcoord = Opt.cbarloc; 305 | hascbcoord = true; 306 | if cbarcoord(3)-cbarcoord(1) < cbarcoord(4)-cbarcoord(2) 307 | isvert = true; 308 | Opt.cbarloc = 'east'; 309 | else 310 | isvert = false; 311 | Opt.cbarloc = 'south'; 312 | end 313 | else 314 | hascbcoord = false; 315 | if any(strcmp(pos([3 4 7 8]), lower(Opt.cbarloc))) 316 | isvert = true; 317 | else 318 | isvert = false; 319 | end 320 | end 321 | 322 | 323 | hout.cb = pcolorbar(clevcbar, cmapcbar, 'even', Opt.evencb, 'location', Opt.cbarloc); 324 | 325 | if ismember(hout.cb.cb.Location, {'north', 'south', 'northoutside', 'southoutside'}) 326 | tk = get(hout.cb.ax, 'xticklabel'); 327 | [tk{[1 end]}] = deal(' '); 328 | set(hout.cb.ax, 'xticklabel', tk); 329 | else 330 | tk = get(hout.cb.ax, 'yticklabel'); 331 | [tk{[1 end]}] = deal(' '); 332 | set(hout.cb.ax, 'yticklabel', tk); 333 | end 334 | 335 | end 336 | drawnow; 337 | 338 | %------------------------ 339 | % Contour calculations 340 | %------------------------ 341 | 342 | axes(ax); 343 | hold(ax, 'on'); 344 | 345 | switch Opt.method 346 | case 'recolor' 347 | if verLessThan('matlab', '8.4.0') 348 | 349 | [c,h] = contourf(x,y,z,clev); 350 | 351 | hpatch = get(h, 'children'); 352 | 353 | if isscalar(hpatch) 354 | cdata = get(hpatch, 'cdata'); 355 | else 356 | cdata = cell2mat(get(hpatch, 'cdata')); 357 | end 358 | 359 | % Mark too-high contours 360 | 361 | isabove = cdata == max(clev); 362 | 363 | % Distinguish between too-lo contours and NaN contours 364 | 365 | if ~any(isnan(z(:))) 366 | isbelow = isnan(cdata); 367 | isn = false(size(cdata)); 368 | else 369 | idxtmp = find(isnan(cdata)); 370 | xy = get(hpatch(idxtmp), {'xdata','ydata'}); 371 | xy = cellfun(@(x) x(1), xy); 372 | if irrflag 373 | F = scatteredInterpolant(x(:), y(:), z(:)); 374 | ztmp = F(xy(:,1),xy(:,2)); 375 | else 376 | ztmp = interp2(x,y,z,xy(:,1),xy(:,2)); 377 | end 378 | 379 | isn = false(size(cdata)); 380 | isn(idxtmp(isnan(ztmp))) = true; 381 | 382 | isbelow = isnan(cdata) & ~isn; 383 | 384 | end 385 | 386 | level = get(h, 'LevelList'); 387 | if ~isequal(level, clev) 388 | error('oops, something new with levels, check this'); 389 | end 390 | 391 | [tf, idx] = ismember(cdata, clev); 392 | 393 | isweirdextra = idx == 0 & ~isbelow & ~isabove; % Why do these show up?! 394 | 395 | for ip = 1:length(hpatch) 396 | if isbelow(ip) 397 | set(hpatch(ip), 'facecolor', Opt.lo); 398 | elseif isabove(ip) 399 | set(hpatch(ip), 'facecolor', Opt.hi); 400 | elseif isn(ip) 401 | set(hpatch(ip), 'facecolor', [1 1 1]); 402 | elseif isweirdextra(ip) 403 | dist = cdata(ip) - clev; 404 | dist(dist<0) = Inf; 405 | [blah, imin] = min(dist); 406 | set(hpatch(ip), 'facecolor', cmap(imin,:)); 407 | else 408 | set(hpatch(ip), 'facecolor', cmap(idx(ip),:)); 409 | end 410 | end 411 | 412 | hout.h = h; 413 | hout.c = c; 414 | 415 | else 416 | 417 | [c,h] = contourf(x,y,z,clev); 418 | 419 | drawnow; 420 | Fp = h.FacePrims; 421 | np = length(Fp); 422 | 423 | % I can't seem to find the property that links each 424 | % TriangleStrip to a particular contour level (and hence the 425 | % appropriate color). So I'm going to have to determine that 426 | % by checking the value in one triangle per strip. 427 | 428 | vd = get(Fp, 'VertexData'); 429 | 430 | % Vertex data is sometimes scaled. I'm not quite sure what 431 | % determines this. My best bet so far is to just check to see 432 | % if the vd data is in the axis limits. If not, assume it's 433 | % scaled. (I used to also check that potentially-scaled values 434 | % were between 0 and 1, but I've hit a few examples where 435 | % scaled values exceed that by a few thousandths... still not 436 | % very clear on what's going on here). 437 | 438 | if ~iscell(vd) 439 | vd = {vd}; 440 | end 441 | lims = [min(cat(2, vd{:}), [], 2) max(cat(2, vd{:}), [], 2)]; 442 | axlims = get(ax, {'xlim', 'ylim','zlim'}); 443 | axlims = cat(1, axlims{:}); 444 | 445 | isin = bsxfun(@ge, lims, axlims(:,1)) & bsxfun(@le, lims, axlims(:,2)); 446 | % is01 = lims >= 0 & lims <= 1; 447 | 448 | if ~all(isin(:)) 449 | % if all(is01(:)) % Scaled 450 | s = diff(axlims,1,2); 451 | o = axlims(:,1); 452 | vd = cellfun(@(v) bsxfun(@plus, bsxfun(@times, double(v), s), o), vd, 'uni', 0); 453 | end 454 | 455 | 456 | sd = get(Fp, 'StripData'); 457 | if ~iscell(sd) 458 | sd = {sd}; 459 | end 460 | xyz = cellfun(@(v,s) double(v(:,s(1:end-1))), vd, sd, 'uni', 0); 461 | idx = zeros(np,1); 462 | 463 | if irrflag 464 | % error('Still figuring this one out for irregular grids'); 465 | F = scatteredInterpolant(x(:), y(:), z(:)); 466 | else 467 | F = griddedInterpolant({x,y},z'); 468 | end 469 | 470 | for ii = 1:np 471 | tmp = F(xyz{ii}(1,:), xyz{ii}(2,:)); 472 | % tmp = interp2(x,y,z,xyz{ii}(1,:), xyz{ii}(2,:)); 473 | [ntmp, bin] = histc(tmp, [-Inf clev Inf]); 474 | [~,idx(ii)] = max(ntmp); 475 | end 476 | 477 | cdata = [Opt.lo; cmap; Opt.hi] * 255; 478 | newcol = cdata(idx,:); 479 | 480 | for ii = 1:np 481 | Fp(ii).ColorData = uint8([newcol(ii,:) 255]'); 482 | end 483 | 484 | hout.h = h; 485 | hout.c = c; 486 | 487 | end 488 | 489 | 490 | case 'calccontour' 491 | 492 | % Get contour lines 493 | 494 | if isvector(x) 495 | x = reshape(x, 1, []); 496 | end 497 | if isvector(y) 498 | y = reshape(y, [], 1); 499 | end 500 | 501 | % Calculate contour lines 502 | 503 | if irrflag 504 | [c, htmp] = contour(x,y,z,clev); 505 | S = contourcs(c, 'cmat'); 506 | delete(htmp); 507 | 508 | F = scatteredInterpolant(x(:), y(:), z(:)); 509 | else 510 | S = contourcs(x,y,z,clev); 511 | F = griddedInterpolant({x,y},z'); 512 | end 513 | 514 | % Coordinates for wall 515 | 516 | if isvector(x) 517 | xlim = [min(x) max(x)]; 518 | ylim = [min(y) max(y)]; 519 | xwall = xlim([1 1 2 2 1]); 520 | ywall = ylim([1 2 2 1 1]); 521 | else 522 | xwall = [x(end:-1:1,1)' x(2,:), x(2:end,end)', x(end,end-1:-1:1)]; 523 | ywall = [y(end:-1:1,1)' y(2,:), y(2:end,end)', y(end,end-1:-1:1)]; 524 | end 525 | 526 | % If there are NaNs in the dataset, calculate contour lines 527 | % for the NaN regions and for the inpainted data 528 | 529 | nflag = any(isnan(z(:))); 530 | if nflag 531 | 532 | % Inpaint the NaN holes using nearest neighbor values 533 | 534 | if irrflag 535 | [zi, A] = fillnan(z, {x,y}); 536 | F = scatteredInterpolant(x(:), y(:), zi(:)); 537 | Fn = scatteredInterpolant(x(:), y(:), double(isnan(z(:)))); 538 | else 539 | [zi, A] = fillnan(z', {x, y}); 540 | zi = zi'; 541 | F = griddedInterpolant({x,y},zi'); 542 | Fn = griddedInterpolant({x,y},double(isnan(z'))); 543 | end 544 | 545 | % Calculate the polygons that surround all NaNs 546 | 547 | [xv,yv] = voronoigrid(x,y); 548 | 549 | xn = xv(isnan(z)); 550 | yn = yv(isnan(z)); 551 | 552 | if useps 553 | pn = polyshape(xn{1}, yn{1}); 554 | for ii = 2:length(xn) 555 | pn = union(pn, polyshape(xn{ii}, yn{ii})); 556 | end 557 | else 558 | [xn,yn] = poly2cw(xn,yn); 559 | [xn, yn] = polyjoin(xn, yn); 560 | end 561 | 562 | % Calculate contours for now-filled dataset 563 | 564 | if irrflag 565 | [c, htmp] = contour(x,y,zi,clev); 566 | S = contourcs(c, 'cmat'); 567 | delete(htmp); 568 | else 569 | S = contourcs(x, y, zi, clev); 570 | end 571 | 572 | end 573 | 574 | 575 | % Extend contours to wall 576 | 577 | S = extendtowall(S,xwall,ywall); 578 | if useps 579 | [xc,yc] = deal(cell(size(S))); 580 | for is = 1:length(S) 581 | pctmp = polyshape(S(is).X, S(is).Y); 582 | [xc{is}, yc{is}] = boundary(pctmp); 583 | end 584 | else 585 | [xc,yc] = poly2cw({S.X}, {S.Y}); 586 | end 587 | 588 | % Remove overlap and triangulate 589 | 590 | if nflag 591 | 592 | [xc, yc] = removeoverlap(xc, yc, xwall, ywall, useps); 593 | for ic = 1:length(xc) 594 | if useps 595 | pc = polyshape(xc{ic}, yc{ic}); 596 | pc = subtract(pc, pn); 597 | [xc{ic}, yc{ic}] = boundary(pc); 598 | else 599 | [xc{ic}, yc{ic}] = polybool('-', xc{ic}, yc{ic}, xn, yn); 600 | end 601 | end 602 | isemp = cellfun(@isempty, xc); 603 | xc = xc(~isemp); 604 | yc = yc(~isemp); 605 | 606 | [f,v,lev] = poly2faces(xc, yc, F, clev, useps); 607 | 608 | else 609 | [xc, yc] = removeoverlap(xc, yc, xwall, ywall, useps); 610 | [f,v,lev] = poly2faces(xc, yc, F, clev, useps); 611 | end 612 | np = length(f); 613 | 614 | 615 | % Plot contour lines and patches 616 | 617 | cmap2 = [Opt.lo; cmap; Opt.hi; 1 1 1]; 618 | 619 | hout.p = gobjects(np,1); 620 | 621 | hold(ax, 'on'); 622 | for ip = 1:np 623 | hout.p(ip) = patch('faces', f{ip}, 'vertices', v{ip}, 'facecolor', cmap2(lev(ip),:), 'edgecolor', 'none'); 624 | end 625 | [hout.c, hout.h] = contour(x,y,z,clev,'k'); 626 | 627 | % A few axes properties changes similar to those made by contourf 628 | 629 | axis(ax, 'tight'); 630 | set(ax, 'layer', 'top', 'box', 'on'); 631 | 632 | end 633 | 634 | if showcb && hascbcoord 635 | set(h.cb.cb, 'position', cbarcoord); 636 | end 637 | 638 | if showcb 639 | uistack(hout.cb.ax, 'top'); % Has to be done after plotting, but resets color in R2014b-recolor... aaargh! 640 | end 641 | 642 | %------------------------ 643 | % Output 644 | %------------------------ 645 | 646 | if nargout > 0 647 | varargout{1} = hout; 648 | end 649 | 650 | if useps 651 | warning(W); 652 | end 653 | 654 | %*********** Subfunctions ************************************************ 655 | 656 | %------------ 657 | % Minmax 658 | %------------ 659 | 660 | % function a = minmax(b) 661 | % a = [min(b(:)) max(b(:))]; 662 | 663 | %-------------------- 664 | % Extend contours to 665 | % "walls" of the grid 666 | %-------------------- 667 | 668 | function S = extendtowall(S, xwall, ywall) 669 | 670 | isclosed = arrayfun(@(A) isequal([A.X(1) A.Y(1)], [A.X(end) A.Y(end)]), S); 671 | 672 | [arclen, seglen] = arclength(xwall(:), ywall(:)); 673 | twall = [0; cumsum(seglen)]./arclen; 674 | 675 | % plot(xwall, ywall); 676 | 677 | for ii = find(~isclosed)' 678 | 679 | % Where along the wall does the open part of the contour hit? 680 | 681 | [xy,d,t] = distance2curve([xwall(:) ywall(:)], [S(ii).X([end 1])', S(ii).Y([end 1])']); 682 | 683 | % plot(S(ii).X, S(ii).Y); 684 | % plot(xy(:,1), xy(:,2), '*'); 685 | 686 | % Figure out which wall points need to be included, if any 687 | 688 | if t(1) < t(2) 689 | isin = twall > t(1) & twall < t(2); 690 | tnew = [t(1); twall(isin); t(2)]; 691 | pt = interparc(tnew, xwall, ywall, 'linear'); 692 | else 693 | isin1 = twall > t(1); 694 | isin2 = twall < t(2); 695 | tnew1 = [t(1); twall(isin1)]; 696 | tnew2 = [twall(isin2); t(2)]; 697 | pt = [interparc(tnew1, xwall, ywall, 'linear'); interparc(tnew2, xwall, ywall, 'linear')]; 698 | 699 | end 700 | 701 | % plot(pt(:,1), pt(:,2), '--'); 702 | 703 | S(ii).X = [S(ii).X(1:end-1) pt(:,1)']; 704 | S(ii).Y = [S(ii).Y(1:end-1) pt(:,2)']; 705 | 706 | % plot(S(ii).X, S(ii).Y, '--'); 707 | 708 | end 709 | 710 | %-------------------- 711 | % Calculate overlap 712 | % and triangulate 713 | %-------------------- 714 | 715 | function [xnew, ynew] = removeoverlap(xc, yc, xwall, ywall, useps) 716 | 717 | % Eliminate any empty contours or duplicates 718 | 719 | if useps 720 | pwall = polyshape(xwall,ywall); % will automatically make external 721 | [xwall, ywall] = boundary(pwall); 722 | else 723 | [xwall, ywall] = poly2cw(xwall, ywall); 724 | end 725 | 726 | xc = [xwall; xc(:)]; 727 | yc = [ywall; yc(:)]; 728 | isemp = cellfun(@isempty, xc); 729 | 730 | xc = xc(~isemp); 731 | yc = yc(~isemp); 732 | 733 | % Calculate overlap 734 | 735 | [xnew, ynew] = multiplepolyint(xc,yc); 736 | 737 | 738 | function [f,v,lev] = poly2faces(xnew, ynew, F, clev, useps) 739 | 740 | np = length(xnew); 741 | 742 | % Triangulate 743 | 744 | [f,v] = deal(cell(np,1)); 745 | if useps 746 | for ip = 1:np 747 | p = polyshape(xnew{ip}, ynew{ip}); 748 | if isempty(p.Vertices) 749 | else 750 | T = triangulation(p); 751 | f{ip} = T.ConnectivityList; 752 | v{ip} = T.Points; 753 | end 754 | end 755 | else 756 | 757 | for ip = 1:np 758 | [f{ip},v{ip}] = poly2fv(xnew{ip}, ynew{ip}); 759 | end 760 | end 761 | isemp = cellfun('isempty', f); 762 | f = f(~isemp); 763 | v = v(~isemp); 764 | np = length(f); 765 | 766 | % There's probably a more elegant way to figure out which color 767 | % goes where, but this works for now 768 | 769 | lev = zeros(np,1); 770 | for ii = 1:np 771 | vx = v{ii}(:,1); 772 | vy = v{ii}(:,2); 773 | fx = mean(vx(f{ii}),2); 774 | fy = mean(vy(f{ii}),2); 775 | 776 | tmp = F(fx,fy); 777 | % tmp = interp2(x,y,z,fx,fy); 778 | [ntmp, bin] = histc(tmp, [-Inf clev Inf]); 779 | if ~any(ntmp) 780 | lev(ii) = length(clev)+2; 781 | else 782 | [~,lev(ii)] = max(ntmp); 783 | end 784 | end 785 | 786 | %-------------------- 787 | % Merge faces that 788 | % share an edge 789 | %-------------------- 790 | 791 | function [xout,yout] = mergefaces(x,y) 792 | 793 | [x,y] = polyjoin(x,y); 794 | [vxy, ~, vidx] = unique([x y], 'rows'); 795 | nvert = sum(~isnan(vxy(:,1))); 796 | isn = all(isnan(vxy),2); 797 | vxy = vxy(1:nvert,:); 798 | vidx(vidx > nvert) = NaN; 799 | 800 | segs = [vidx(1:end-1) vidx(2:end)]; 801 | isn = any(isnan(segs),2); 802 | segs = segs(~isn,:); 803 | 804 | isdup = ismember(segs, fliplr(segs), 'rows'); 805 | 806 | 807 | xseg = [x(1:end-1) x(2:end)]; 808 | yseg = [y(1:end-1) y(2:end)]; 809 | 810 | isn = any(isnan(xseg),2); 811 | xseg = xseg(~isn,:); 812 | yseg = yseg(~isn,:); 813 | 814 | xyunq = unique([xseg yseg], 'rows'); 815 | 816 | 817 | isdup = ismember([xseg yseg], [fliplr(xseg) fliplr(yseg)], 'rows'); 818 | 819 | %-------------------- 820 | % Voronoi regions for 821 | % each point 822 | %-------------------- 823 | 824 | function [xv,yv] = voronoigrid(x,y) 825 | 826 | if isvector(x) && isvector(y) 827 | [xg, yg] = meshgrid(x,y); 828 | else 829 | xg = x; 830 | yg = y; 831 | end 832 | 833 | [nr,nc] = size(xg); 834 | 835 | 836 | xmid = filter2([1 1 ; 1 1]./4, padarray(xg, [1 1], 'replicate', 'both'), 'valid'); 837 | ymid = filter2([1 1 ; 1 1]./4, padarray(yg, [1 1], 'replicate', 'both'), 'valid'); 838 | 839 | [xv,yv] = deal(cell(size(xg))); 840 | for ir = 1:nr 841 | for ic = 1:nc 842 | ridx = [0 1 1 0 0]+ir; 843 | cidx = [0 0 1 1 0]+ic; 844 | idx = sub2ind([nr+1 nc+1], ridx, cidx); 845 | xv{ir,ic} = xmid(idx); 846 | yv{ir,ic} = ymid(idx); 847 | end 848 | end 849 | 850 | 851 | -------------------------------------------------------------------------------- /distance2curve/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | ehthumbs.db 37 | Thumbs.db -------------------------------------------------------------------------------- /distance2curve/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, John D'Errico 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /fillnan/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | ehthumbs.db 37 | Thumbs.db -------------------------------------------------------------------------------- /fillnan/fillnan.m: -------------------------------------------------------------------------------- 1 | function [v, A] = fillnan(v, coords, varargin) 2 | %FILLNAN Fill in missing values in an array with nearest neighbor 3 | % 4 | % [v, A] = fillnan(v) 5 | % [v, A] = fillnan(v, coords, p1, v1, ...) 6 | % 7 | % This function fills NaN (or otherwise masked) points in a gridded array 8 | % with the value from the nearest non-masked grid point. It is much faster 9 | % than using a scattered interpolant for this, or inpainting functions like 10 | % inpaint_nans (which perform more complex fitting calculations). The 11 | % output also allows for resuse of the nearest-neighbor calculation on 12 | % different datasets that share a coordinate system. 13 | % 14 | % Input variables: 15 | % 16 | % v: array of values, 2D or 3D array, nd-grid style 17 | % 18 | % coords: 1 x 2 or 1 x 3 cell array of grid coordinates, corresponding to 19 | % x, y, and z coordinates, respectively. Cells should either be 20 | % the same size as v, or vectors following ndgrid conventions. 21 | % If empty or not included, coordinates will correspond to array 22 | % indices. 23 | % 24 | % Optional input variables (passed as parameter/value pairs): 25 | % 26 | % mask: mask indicating where data is missing (1) and present (0). If 27 | % not included, defaults to isnan(v) 28 | % 29 | % geo: true if input coordinates represent geographic coordinates 30 | % {longitude, latitude, height (m)}, rather than cartesian 31 | % {x,y,z} coordinates. [false] 32 | % 33 | % perim: true to fill only values along the edge of data/no-data 34 | % boundaries. This option is useful when preparing to upsample a 35 | % dataset when you only need to prevent propagation of NaNs along 36 | % the edges of a mask (for example, upsampling ocean data where 37 | % land is masked), without wasting computation time on filling 38 | % inland grid cells. [false] 39 | % 40 | % Output variables: 41 | % 42 | % v: array matching input v, with all NaNs (or NaNs bordering data) 43 | % now filled in. 44 | % 45 | % A: 1 x 1 structure with the following fields. Can be used to 46 | % repeat interpolation without recalculating nearest neighbors, 47 | % e.g. v2(A.idxnn) = v2(A.idxfill). 48 | % 49 | % idxfill: indices to the missing-data cells that are 50 | % filled in 51 | % 52 | % idxnn: indices to the nearest-neighbor data cells used to 53 | % fill in each of the cells in the above array, 54 | % respectively. 55 | 56 | % Copyright 2013-2015 Kelly Kearney 57 | 58 | %------------------------ 59 | % Parse input 60 | %------------------------ 61 | 62 | p = inputParser; 63 | p.addParameter('mask', isnan(v)); 64 | p.addParameter('geo', false); 65 | p.addParameter('perim', false); 66 | p.parse(varargin{:}); 67 | Opt = p.Results; 68 | 69 | if ndims(v) > 3 70 | error('Only 2D and 3D arrays supported at this time'); 71 | end 72 | [nx, ny, nz] = size(v); 73 | 74 | % Convert coordinates if necessary 75 | 76 | if nargin < 2 || isempty(coords) 77 | coords = {1:nx, 1:ny, 1:nz}; 78 | end 79 | 80 | if cellfun(@isvector, coords) % all implicit 81 | [coords{:}] = ndgrid(coords{:}); 82 | end 83 | 84 | if Opt.geo 85 | 86 | E = referenceEllipsoid('earth'); % WGS84, m 87 | 88 | phi = degtorad(coords{1}); 89 | lambda = degtorad(coords{2}); 90 | if length(coords) == 3 91 | h = coords{3}; 92 | else 93 | h = zeros(size(phi)); 94 | end 95 | [x,y,z] = geodetic2ecef(phi, lambda, h, E); 96 | else 97 | x = coords{1}; 98 | y = coords{2}; 99 | if length(coords) == 3 100 | z = coords{3}; 101 | else 102 | z = zeros(size(x)); 103 | end 104 | end 105 | 106 | %------------------------ 107 | % Match no-data points to 108 | % data points 109 | %------------------------ 110 | 111 | if Opt.perim 112 | 113 | % Find perimeter values 114 | 115 | mask = padarray(Opt.mask, [1 1 1], 'both', 'replicate'); % Pad so box isn't considered boundary 116 | bwp = bwperim(mask); 117 | bwp = bwp(2:end-1, 2:end-1, 2:end-1); 118 | 119 | % Dilate perimeter in all directions 120 | 121 | bwd = imdilate(bwp, ones(3,3,3)); 122 | 123 | % Match data-perimeter values to missing-perimeter values 124 | 125 | is1 = bwd & Opt.mask; % perimeter w/o data 126 | is2 = bwd & ~Opt.mask; % perimeter w/ data 127 | 128 | else 129 | 130 | is1 = Opt.mask; 131 | is2 = ~Opt.mask; 132 | 133 | end 134 | 135 | idx = knnsearch([x(is2) y(is2) z(is2)], [x(is1) y(is1) z(is1)]); 136 | 137 | A.idxfill = find(is1); 138 | tmp = find(is2); 139 | A.idxnn = tmp(idx); 140 | 141 | % Fill with nearest neighbor 142 | 143 | v(A.idxfill) = v(A.idxnn); 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /interparc/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | ehthumbs.db 37 | Thumbs.db -------------------------------------------------------------------------------- /interparc/interparc.m: -------------------------------------------------------------------------------- 1 | function [pt,dudt,fofthandle] = interparc(t,px,py,varargin) 2 | % interparc: interpolate points along a curve in 2 or more dimensions 3 | % usage: pt = interparc(t,px,py) % a 2-d curve 4 | % usage: pt = interparc(t,px,py,pz) % a 3-d curve 5 | % usage: pt = interparc(t,px,py,pz,pw,...) % a 4-d or higher dimensional curve 6 | % usage: pt = interparc(t,px,py,method) % a 2-d curve, method is specified 7 | % usage: [pt,dudt,fofthandle] = interparc(t,px,py,...) % also returns derivatives, and a function handle 8 | % 9 | % Interpolates new points at any fractional point along 10 | % the curve defined by a list of points in 2 or more 11 | % dimensions. The curve may be defined by any sequence 12 | % of non-replicated points. 13 | % 14 | % arguments: (input) 15 | % t - vector of numbers, 0 <= t <= 1, that define 16 | % the fractional distance along the curve to 17 | % interpolate the curve at. t = 0 will generate 18 | % the very first point in the point list, and 19 | % t = 1 yields the last point in that list. 20 | % Similarly, t = 0.5 will yield the mid-point 21 | % on the curve in terms of arc length as the 22 | % curve is interpolated by a parametric spline. 23 | % 24 | % If t is a scalar integer, at least 2, then 25 | % it specifies the number of equally spaced 26 | % points in arclength to be generated along 27 | % the curve. 28 | % 29 | % px, py, pz, ... - vectors of length n, defining 30 | % points along the curve. n must be at least 2. 31 | % Exact Replicate points should not be present 32 | % in the curve, although there is no constraint 33 | % that the curve has replicate independent 34 | % variables. 35 | % 36 | % method - (OPTIONAL) string flag - denotes the method 37 | % used to compute the points along the curve. 38 | % 39 | % method may be any of 'linear', 'spline', or 'pchip', 40 | % or any simple contraction thereof, such as 'lin', 41 | % 'sp', or even 'p'. 42 | % 43 | % method == 'linear' --> Uses a linear chordal 44 | % approximation to interpolate the curve. 45 | % This method is the most efficient. 46 | % 47 | % method == 'pchip' --> Uses a parametric pchip 48 | % approximation for the interpolation 49 | % in arc length. 50 | % 51 | % method == 'spline' --> Uses a parametric spline 52 | % approximation for the interpolation in 53 | % arc length. Generally for a smooth curve, 54 | % this method may be most accurate. 55 | % 56 | % method = 'csape' --> if available, this tool will 57 | % allow a periodic spline fit for closed curves. 58 | % ONLY use this method if your points should 59 | % represent a closed curve. 60 | % 61 | % If the last point is NOT the same as the 62 | % first point on the curve, then the curve 63 | % will be forced to be periodic by this option. 64 | % That is, the first point will be replicated 65 | % onto the end. 66 | % 67 | % If csape is not present in your matlab release, 68 | % then an error will result. 69 | % 70 | % DEFAULT: 'spline' 71 | % 72 | % 73 | % arguments: (output) 74 | % pt - Interpolated points at the specified fractional 75 | % distance (in arc length) along the curve. 76 | % 77 | % dudt - when a second return argument is required, 78 | % interparc will return the parametric derivatives 79 | % (dx/dt, dy/dt, dz/dt, ...) as an array. 80 | % 81 | % fofthandle - a function handle, taking numbers in the interval [0,1] 82 | % and evaluating the function at those points. 83 | % 84 | % Extrapolation will not be permitted by this call. 85 | % Any values of t that lie outside of the interval [0,1] 86 | % will be clipped to the endpoints of the curve. 87 | % 88 | % Example: 89 | % % Interpolate a set of unequally spaced points around 90 | % % the perimeter of a unit circle, generating equally 91 | % % spaced points around the perimeter. 92 | % theta = sort(rand(15,1))*2*pi; 93 | % theta(end+1) = theta(1); 94 | % px = cos(theta); 95 | % py = sin(theta); 96 | % 97 | % % interpolate using parametric splines 98 | % pt = interparc(100,px,py,'spline'); 99 | % 100 | % % Plot the result 101 | % plot(px,py,'r*',pt(:,1),pt(:,2),'b-o') 102 | % axis([-1.1 1.1 -1.1 1.1]) 103 | % axis equal 104 | % grid on 105 | % xlabel X 106 | % ylabel Y 107 | % title 'Points in blue are uniform in arclength around the circle' 108 | % 109 | % 110 | % Example: 111 | % % For the previous set of points, generate exactly 6 112 | % % points around the parametric splines, verifying 113 | % % the uniformity of the arc length interpolant. 114 | % pt = interparc(6,px,py,'spline'); 115 | % 116 | % % Convert back to polar form. See that the radius 117 | % % is indeed 1, quite accurately. 118 | % [TH,R] = cart2pol(pt(:,1),pt(:,2)) 119 | % % TH = 120 | % % 0.86005 121 | % % 2.1141 122 | % % -2.9117 123 | % % -1.654 124 | % % -0.39649 125 | % % 0.86005 126 | % % R = 127 | % % 1 128 | % % 0.9997 129 | % % 0.9998 130 | % % 0.99999 131 | % % 1.0001 132 | % % 1 133 | % 134 | % % Unwrap the polar angles, and difference them. 135 | % diff(unwrap(TH)) 136 | % % ans = 137 | % % 1.2541 138 | % % 1.2573 139 | % % 1.2577 140 | % % 1.2575 141 | % % 1.2565 142 | % 143 | % % Six points around the circle should be separated by 144 | % % 2*pi/5 radians, if they were perfectly uniform. The 145 | % % slight differences are due to the imperfect accuracy 146 | % % of the parametric splines. 147 | % 2*pi/5 148 | % % ans = 149 | % % 1.2566 150 | % 151 | % 152 | % See also: arclength, spline, pchip, interp1 153 | % 154 | % Author: John D'Errico 155 | % e-mail: woodchips@rochester.rr.com 156 | % Release: 1.0 157 | % Release date: 3/15/2010 158 | 159 | % unpack the arguments and check for errors 160 | if nargin < 3 161 | error('ARCLENGTH:insufficientarguments', ... 162 | 'at least t, px, and py must be supplied') 163 | end 164 | 165 | t = t(:); 166 | if (numel(t) == 1) && (t > 1) && (rem(t,1) == 0) 167 | % t specifies the number of points to be generated 168 | % equally spaced in arclength 169 | t = linspace(0,1,t)'; 170 | elseif any(t < 0) || any(t > 1) 171 | error('ARCLENGTH:impropert', ... 172 | 'All elements of t must be 0 <= t <= 1') 173 | end 174 | 175 | % how many points will be interpolated? 176 | nt = numel(t); 177 | 178 | % the number of points on the curve itself 179 | px = px(:); 180 | py = py(:); 181 | n = numel(px); 182 | 183 | % are px and py both vectors of the same length? 184 | if ~isvector(px) || ~isvector(py) || (length(py) ~= n) 185 | error('ARCLENGTH:improperpxorpy', ... 186 | 'px and py must be vectors of the same length') 187 | elseif n < 2 188 | error('ARCLENGTH:improperpxorpy', ... 189 | 'px and py must be vectors of length at least 2') 190 | end 191 | 192 | % compose px and py into a single array. this way, 193 | % if more dimensions are provided, the extension 194 | % is trivial. 195 | pxy = [px,py]; 196 | ndim = 2; 197 | 198 | % the default method is 'linear' 199 | method = 'spline'; 200 | 201 | % are there any other arguments? 202 | if nargin > 3 203 | % there are. check the last argument. Is it a string? 204 | if ischar(varargin{end}) 205 | method = varargin{end}; 206 | varargin(end) = []; 207 | 208 | % method may be any of {'linear', 'pchip', 'spline', 'csape'.} 209 | % any any simple contraction thereof. 210 | valid = {'linear', 'pchip', 'spline', 'csape'}; 211 | [method,errstr] = validstring(method,valid); 212 | if ~isempty(errstr) 213 | error('INTERPARC:incorrectmethod',errstr) 214 | end 215 | end 216 | 217 | % anything that remains in varargin must add 218 | % an additional dimension on the curve/polygon 219 | for i = 1:numel(varargin) 220 | pz = varargin{i}; 221 | pz = pz(:); 222 | if numel(pz) ~= n 223 | error('ARCLENGTH:improperpxorpy', ... 224 | 'pz must be of the same size as px and py') 225 | end 226 | pxy = [pxy,pz]; %#ok 227 | end 228 | 229 | % the final number of dimensions provided 230 | ndim = size(pxy,2); 231 | end 232 | 233 | % if csape, then make sure the first point is replicated at the end. 234 | % also test to see if csape is available 235 | if method(1) == 'c' 236 | if exist('csape','file') == 0 237 | error('CSAPE was requested, but you lack the necessary toolbox.') 238 | end 239 | 240 | p1 = pxy(1,:); 241 | pend = pxy(end,:); 242 | 243 | % get a tolerance on whether the first point is replicated. 244 | if norm(p1 - pend) > 10*eps(norm(max(abs(pxy),[],1))) 245 | % the two end points were not identical, so wrap the curve 246 | pxy(end+1,:) = p1; 247 | nt = nt + 1; 248 | end 249 | end 250 | 251 | % preallocate the result, pt 252 | pt = NaN(nt,ndim); 253 | 254 | % Compute the chordal (linear) arclength 255 | % of each segment. This will be needed for 256 | % any of the methods. 257 | chordlen = sqrt(sum(diff(pxy,[],1).^2,2)); 258 | 259 | % Normalize the arclengths to a unit total 260 | chordlen = chordlen/sum(chordlen); 261 | 262 | % cumulative arclength 263 | cumarc = [0;cumsum(chordlen)]; 264 | 265 | % The linear interpolant is trivial. do it as a special case 266 | if method(1) == 'l' 267 | % The linear method. 268 | 269 | % which interval did each point fall in, in 270 | % terms of t? 271 | [junk,tbins] = histc(t,cumarc); %#ok 272 | 273 | % catch any problems at the ends 274 | tbins((tbins <= 0) | (t <= 0)) = 1; 275 | tbins((tbins >= n) | (t >= 1)) = n - 1; 276 | 277 | % interpolate 278 | s = (t - cumarc(tbins))./chordlen(tbins); 279 | % be nice, and allow the code to work on older releases 280 | % that don't have bsxfun 281 | pt = pxy(tbins,:) + (pxy(tbins+1,:) - pxy(tbins,:)).*repmat(s,1,ndim); 282 | 283 | % do we need to compute derivatives here? 284 | if nargout > 1 285 | dudt = (pxy(tbins+1,:) - pxy(tbins,:))./repmat(chordlen(tbins),1,ndim); 286 | end 287 | 288 | % do we need to create the spline as a piecewise linear function? 289 | if nargout > 2 290 | spl = cell(1,ndim); 291 | for i = 1:ndim 292 | coefs = [diff(pxy(:,i))./diff(cumarc),pxy(1:(end-1),i)]; 293 | spl{i} = mkpp(cumarc.',coefs); 294 | end 295 | 296 | %create a function handle for evaluation, passing in the splines 297 | fofthandle = @(t) foft(t,spl); 298 | end 299 | 300 | % we are done at this point 301 | return 302 | end 303 | 304 | % If we drop down to here, we have either a spline 305 | % or csape or pchip interpolant to work with. 306 | 307 | % compute parametric splines 308 | spl = cell(1,ndim); 309 | spld = spl; 310 | diffarray = [3 0 0;0 2 0;0 0 1;0 0 0]; 311 | for i = 1:ndim 312 | switch method 313 | case 'pchip' 314 | spl{i} = pchip(cumarc,pxy(:,i)); 315 | case 'spline' 316 | spl{i} = spline(cumarc,pxy(:,i)); 317 | nc = numel(spl{i}.coefs); 318 | if nc < 4 319 | % just pretend it has cubic segments 320 | spl{i}.coefs = [zeros(1,4-nc),spl{i}.coefs]; 321 | spl{i}.order = 4; 322 | end 323 | case 'csape' 324 | % csape was specified, so the curve is presumed closed, 325 | % therefore periodic 326 | spl{i} = csape(cumarc,pxy(:,i),'periodic'); 327 | nc = numel(spl{i}.coefs); 328 | if nc < 4 329 | % just pretend it has cubic segments 330 | spl{i}.coefs = [zeros(1,4-nc),spl{i}.coefs]; 331 | spl{i}.order = 4; 332 | end 333 | end 334 | 335 | % and now differentiate them 336 | xp = spl{i}; 337 | xp.coefs = xp.coefs*diffarray; 338 | xp.order = 3; 339 | spld{i} = xp; 340 | end 341 | 342 | % catch the case where there were exactly three points 343 | % in the curve, and spline was used to generate the 344 | % interpolant. In this case, spline creates a curve with 345 | % only one piece, not two. 346 | if (numel(cumarc) == 3) && (method(1) == 's') 347 | cumarc = spl{1}.breaks; 348 | n = numel(cumarc); 349 | chordlen = sum(chordlen); 350 | end 351 | 352 | % Generate the total arclength along the curve 353 | % by integrating each segment and summing the 354 | % results. The integration scheme does its job 355 | % using an ode solver. 356 | 357 | % polyarray here contains the derivative polynomials 358 | % for each spline in a given segment 359 | polyarray = zeros(ndim,3); 360 | seglen = zeros(n-1,1); 361 | 362 | % options for ode45 363 | opts = odeset('reltol',1.e-9); 364 | for i = 1:spl{1}.pieces 365 | % extract polynomials for the derivatives 366 | for j = 1:ndim 367 | polyarray(j,:) = spld{j}.coefs(i,:); 368 | end 369 | 370 | % integrate the arclength for the i'th segment 371 | % using ode45 for the integral. I could have 372 | % done this part with quad too, but then it 373 | % would not have been perfectly (numerically) 374 | % consistent with the next operation in this tool. 375 | [tout,yout] = ode45(@(t,y) segkernel(t,y),[0,chordlen(i)],0,opts); %#ok 376 | seglen(i) = yout(end); 377 | end 378 | 379 | % and normalize the segments to have unit total length 380 | totalsplinelength = sum(seglen); 381 | cumseglen = [0;cumsum(seglen)]; 382 | 383 | % which interval did each point fall into, in 384 | % terms of t, but relative to the cumulative 385 | % arc lengths along the parametric spline? 386 | [junk,tbins] = histc(t*totalsplinelength,cumseglen); %#ok 387 | 388 | % catch any problems at the ends 389 | tbins((tbins <= 0) | (t <= 0)) = 1; 390 | tbins((tbins >= n) | (t >= 1)) = n - 1; 391 | 392 | % Do the fractional integration within each segment 393 | % for the interpolated points. t is the parameter 394 | % used to define the splines. It is defined in terms 395 | % of a linear chordal arclength. This works nicely when 396 | % a linear piecewise interpolant was used. However, 397 | % what is asked for is an arclength interpolation 398 | % in terms of arclength of the spline itself. Call s 399 | % the arclength traveled along the spline. 400 | s = totalsplinelength*t; 401 | 402 | % the ode45 options will now include an events property 403 | % so we can catch zero crossings. 404 | opts = odeset('reltol',1.e-9,'events',@ode_events); 405 | 406 | ti = t; 407 | for i = 1:nt 408 | % si is the piece of arc length that we will look 409 | % for in this spline segment. 410 | si = s(i) - cumseglen(tbins(i)); 411 | 412 | % extract polynomials for the derivatives 413 | % in the interval the point lies in 414 | for j = 1:ndim 415 | polyarray(j,:) = spld{j}.coefs(tbins(i),:); 416 | end 417 | 418 | % we need to integrate in t, until the integral 419 | % crosses the specified value of si. Because we 420 | % have defined totalsplinelength, the lengths will 421 | % be normalized at this point to a unit length. 422 | % 423 | % Start the ode solver at -si, so we will just 424 | % look for an event where y crosses zero. 425 | [tout,yout,te,ye] = ode45(@(t,y) segkernel(t,y),[0,chordlen(tbins(i))],-si,opts); %#ok 426 | 427 | % we only need that point where a zero crossing occurred 428 | % if no crossing was found, then we can look at each end. 429 | if ~isempty(te) 430 | ti(i) = te(1) + cumarc(tbins(i)); 431 | else 432 | % a crossing must have happened at the very 433 | % beginning or the end, and the ode solver 434 | % missed it, not trapping that event. 435 | if abs(yout(1)) < abs(yout(end)) 436 | % the event must have been at the start. 437 | ti(i) = tout(1) + cumarc(tbins(i)); 438 | else 439 | % the event must have been at the end. 440 | ti(i) = tout(end) + cumarc(tbins(i)); 441 | end 442 | end 443 | end 444 | 445 | % Interpolate the parametric splines at ti to get 446 | % our interpolated value. 447 | for L = 1:ndim 448 | pt(:,L) = ppval(spl{L},ti); 449 | end 450 | 451 | % do we need to compute first derivatives here at each point? 452 | if nargout > 1 453 | dudt = zeros(nt,ndim); 454 | for L = 1:ndim 455 | dudt(:,L) = ppval(spld{L},ti); 456 | end 457 | end 458 | 459 | % create a function handle for evaluation, passing in the splines 460 | if nargout > 2 461 | fofthandle = @(t) foft(t,spl); 462 | end 463 | 464 | % =============================================== 465 | % nested function for the integration kernel 466 | % =============================================== 467 | function val = segkernel(t,y) %#ok 468 | % sqrt((dx/dt)^2 + (dy/dt)^2 + ...) 469 | val = zeros(size(t)); 470 | for k = 1:ndim 471 | val = val + polyval(polyarray(k,:),t).^2; 472 | end 473 | val = sqrt(val); 474 | 475 | end % function segkernel 476 | 477 | % =============================================== 478 | % nested function for ode45 integration events 479 | % =============================================== 480 | function [value,isterminal,direction] = ode_events(t,y) %#ok 481 | % ode event trap, looking for zero crossings of y. 482 | value = y; 483 | isterminal = ones(size(y)); 484 | direction = ones(size(y)); 485 | end % function ode_events 486 | 487 | end % mainline - interparc 488 | 489 | 490 | % =============================================== 491 | % end mainline - interparc 492 | % =============================================== 493 | % begin subfunctions 494 | % =============================================== 495 | 496 | % =============================================== 497 | % subfunction for evaluation at any point externally 498 | % =============================================== 499 | function f_t = foft(t,spl) 500 | % tool allowing the user to evaluate the interpolant at any given point for any values t in [0,1] 501 | pdim = numel(spl); 502 | f_t = zeros(numel(t),pdim); 503 | 504 | % convert t to a column vector, clipping it to [0,1] as we do. 505 | t = max(0,min(1,t(:))); 506 | 507 | % just loop over the splines in the cell array of splines 508 | for i = 1:pdim 509 | f_t(:,i) = ppval(spl{i},t); 510 | end 511 | end % function foft 512 | 513 | 514 | function [str,errorclass] = validstring(arg,valid) 515 | % validstring: compares a string against a set of valid options 516 | % usage: [str,errorclass] = validstring(arg,valid) 517 | % 518 | % If a direct hit, or any unambiguous shortening is found, that 519 | % string is returned. Capitalization is ignored. 520 | % 521 | % arguments: (input) 522 | % arg - character string, to be tested against a list 523 | % of valid choices. Capitalization is ignored. 524 | % 525 | % valid - cellstring array of alternative choices 526 | % 527 | % Arguments: (output) 528 | % str - string - resulting choice resolved from the 529 | % list of valid arguments. If no unambiguous 530 | % choice can be resolved, then str will be empty. 531 | % 532 | % errorclass - string - A string argument that explains 533 | % the error. It will be one of the following 534 | % possibilities: 535 | % 536 | % '' --> No error. An unambiguous match for arg 537 | % was found among the choices. 538 | % 539 | % 'No match found' --> No match was found among 540 | % the choices provided in valid. 541 | % 542 | % 'Ambiguous argument' --> At least two ambiguous 543 | % matches were found among those provided 544 | % in valid. 545 | % 546 | % 547 | % Example: 548 | % valid = {'off' 'on' 'The sky is falling'} 549 | % 550 | % 551 | % See also: parse_pv_pairs, strmatch, strcmpi 552 | % 553 | % Author: John D'Errico 554 | % e-mail: woodchips@rochester.rr.com 555 | % Release: 1.0 556 | % Release date: 3/25/2010 557 | 558 | ind = find(strncmpi(lower(arg),valid,numel(arg))); 559 | if isempty(ind) 560 | % No hit found 561 | errorclass = 'No match found'; 562 | str = ''; 563 | elseif (length(ind) > 1) 564 | % Ambiguous arg, hitting more than one of the valid options 565 | errorclass = 'Ambiguous argument'; 566 | str = ''; 567 | return 568 | else 569 | errorclass = ''; 570 | str = valid{ind}; 571 | end 572 | 573 | end % function validstring 574 | 575 | -------------------------------------------------------------------------------- /interparc/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, John D'Errico 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /multiplepolyint/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | ehthumbs.db 37 | Thumbs.db -------------------------------------------------------------------------------- /multiplepolyint/multiplepolyint.m: -------------------------------------------------------------------------------- 1 | function [xnew, ynew, indices] = multiplepolyint(x, y, varargin) 2 | %MULTIPLEPOLYINT Multiple polygon intersection 3 | % 4 | % [xnew, ynew, indices] = multiplepolyint(x, y) 5 | % [xnew, ynew, indices] = multiplepolyint(x, y, p1, v1, ...) 6 | % 7 | % Determines the regions where polygons overlap, distinguishing between 8 | % each set of overlaps. 9 | % 10 | % Input variables: 11 | % 12 | % x: cell array of x vertices for each input polygon 13 | % 14 | % y: cell array of y vertices for each input polygon 15 | % 16 | % Optional input parameters (passed as parameter/value pairs) 17 | % 18 | % method: method to use for polygon boolean operations: 19 | % 'polybool': use polybool function, default for pre-2017b 20 | % (correponds to older fast = false option) 21 | % 'gpcmex': call gpcmex directly. Faster than polybool 22 | % option in some older versions of Matlab (corresponds to 23 | % older fast = true option) 24 | % 'polyshape': use polyshape object methods, default for 25 | % R2017b+ 26 | % 27 | % v2gpcpath: path to your local copy of vectorsToGPC.m. The default is 28 | % [matlabroot]/toolbox/map/map/private/vectorsToGPC.m, where 29 | % [matlabroot] is the directory where the MATLAB software is 30 | % installed. 31 | % 32 | % gpcmexpath: path to your local copy of the gpcmex mex file. The 33 | % default is 34 | % [matlabroot]/toolbox/map/map/private/gpcmex.[mex], where 35 | % [matlabroot] is the directory where the MATLAB software is 36 | % installed, and [mex] is the appropriate mex file extension 37 | % for your operating system. 38 | % 39 | % vfgpcpath: path to your local copy of vectorsFromGPC.m. The default 40 | % is [matlabroot]/toolbox/map/map/private/vectorsFromGPC.m, 41 | % where [matlabroot] is the directory where the MATLAB 42 | % software is installed. 43 | % 44 | % fast: This is a mostly-deprecated option. If no method is 45 | % specified and Matlab detects pre-2017b is running, this 46 | % logical scalar will switch between the polybool (false) and 47 | % gpcmex (true) methods. 48 | % 49 | % Note: Can also be passed via the older syntax: 50 | % [xnew,ynew,ind] = multiplepolyint(x,y,fastflag) 51 | % This option maintains back-compatibility with older 52 | % versions of this function. 53 | % 54 | % Output variables: 55 | % 56 | % xnew: cell array of x vertices for each resulting polygon 57 | % 58 | % ynew: cell array of y vertices for each resulting polygon 59 | % 60 | % indices: cell array holding indices of input polygons that 61 | % correspond to each output polygon. For example, the output 62 | % polygon resulting from the overlap of polygons 1 and 2 will 63 | % have indices [1 2]. 64 | 65 | % Copyright 2006-2015 Kelly Kearney 66 | 67 | %------------------------------ 68 | % Check input 69 | %------------------------------ 70 | 71 | if nargin == 3 % For old syntax 72 | Opt.fast = varargin{1}; 73 | Opt.v2gpcpath = ''; 74 | Opt.gpcmexpath = ''; 75 | Opt.vfgpcpath = ''; 76 | validateattributes(Opt.fast, {'logical'}, {'scalar'}); 77 | else 78 | p = inputParser; 79 | 80 | p.addParameter('fast', false, @(x) validateattributes(x, {'logical'}, {'scalar'})); 81 | p.addParameter('v2gpcpath', '', @(x) validateattributes(x, {'char'}, {})); 82 | p.addParameter('gpcmexpath', '', @(x) validateattributes(x, {'char'}, {})); 83 | p.addParameter('vfgpcpath', '', @(x) validateattributes(x, {'char'}, {})); 84 | p.addParameter('method', '', @(x) validateattributes(x, {'char'}, {})); 85 | p.parse(varargin{:}); 86 | 87 | Opt = p.Results; 88 | end 89 | 90 | if ~iscell(x) || ~iscell(y) || ~isequal(size(x), size(y)) 91 | error('x and y must be cell arrays with the same dimensions'); 92 | end 93 | if ~all(cellfun(@isvector, x)) || ~all(cellfun(@isvector, y)) 94 | error('Contents of x and y must be vectors'); 95 | end 96 | 97 | if isempty(Opt.method) 98 | if verLessThan('matlab', '9.3.0') % 2017b 99 | if Opt.fast 100 | Opt.method = 'gpcmex'; 101 | else 102 | Opt.method = 'polybool'; 103 | end 104 | else 105 | Opt.method = 'polyshape'; 106 | end 107 | end 108 | validatestring(Opt.method, {'gpcmex', 'polybool', 'polyshape'}); 109 | 110 | x = x(:); 111 | y = y(:); 112 | 113 | % If the fast flag is on, we need access to two functions hidden in a 114 | % private directory of the mapping toolbox. 115 | 116 | if strcmp(Opt.method, 'gpcmex') 117 | 118 | msg = ['Could not find copies of vectorsToGPC.m (%s) and/or ', ... 119 | 'mex function gpcmex (%s). Please verify these paths ', ... 120 | '(if you passed them as inputs) or provide the ', ... 121 | 'appropriate paths (if the default is not ', ... 122 | 'working). The proper files should be found in the ', ... 123 | 'toolbox/map/map/private folder under the ', ... 124 | 'directory where Matlab is installed']; 125 | 126 | mappath = fullfile(matlabroot, 'toolbox', 'map', 'map', 'private'); 127 | if isempty(Opt.v2gpcpath) 128 | Opt.v2gpcpath = fullfile(mappath, 'vectorsToGPC.m'); 129 | end 130 | if isempty(Opt.vfgpcpath) 131 | Opt.vfgpcpath = fullfile(mappath, 'vectorsFromGPC.m'); 132 | end 133 | 134 | if isempty(Opt.gpcmexpath) 135 | Gpc = dir(fullfile(mappath, 'gpcmex*')); 136 | if length(Gpc) < 1 137 | error('multiplepolyint:gpcmex', 'Could not find gpcmex in default location (%s); please pass as input', mappath); 138 | end 139 | if length(Gpc) > 1 140 | warning('Mutiple gpcmex files found; using %s; to change, include path as input parameter', Gpc(1).name); 141 | Gpc = Gpc(1); 142 | end 143 | Opt.gpcmexpath = fullfile(mappath, Gpc.name); 144 | 145 | end 146 | 147 | if ~exist(Opt.vfgpcpath, 'file') || ~exist(Opt.gpcmexpath, 'file') 148 | error('multiplepolyint:privatepath', msg, Opt.v2gpcpath, Opt.gpcmexpath); 149 | end 150 | 151 | vectorsToGPC = function_handle(Opt.v2gpcpath); 152 | vectorsFromGPC = function_handle(Opt.vfgpcpath); 153 | gpcmex = function_handle(Opt.gpcmexpath); 154 | end 155 | 156 | %------------------------------ 157 | % Find intersections 158 | %------------------------------ 159 | 160 | switch Opt.method 161 | case 'polybool' % The original way, using polybool 162 | 163 | xnew = x(1); 164 | ynew = y(1); 165 | indices = {1}; 166 | 167 | for ipoly = 2:length(x) 168 | 169 | x1 = x{ipoly}; 170 | y1 = y{ipoly}; 171 | 172 | for icomp = 1:length(xnew) 173 | 174 | x2 = xnew{icomp}; 175 | y2 = ynew{icomp}; 176 | 177 | % Intersecting 178 | 179 | [xint{icomp}, yint{icomp}] = polybool('&', x1, y1, x2, y2); 180 | 181 | noint = isempty(xint{icomp}); 182 | 183 | % Only in 2 184 | 185 | if noint 186 | xxor{icomp} = x2; 187 | yxor{icomp} = y2; 188 | else 189 | [xxor{icomp}, yxor{icomp}] = polybool('xor', x2, y2, xint{icomp}, yint{icomp}); 190 | end 191 | 192 | noxor = isempty(xxor{icomp}); 193 | 194 | 195 | % Indices 196 | 197 | if noint 198 | indint{icomp} = []; 199 | else 200 | indint{icomp} = [indices{icomp} ipoly]; 201 | end 202 | 203 | if noxor 204 | indxor{icomp} = []; 205 | else 206 | indxor{icomp} = indices{icomp}; 207 | end 208 | 209 | end 210 | 211 | % Only in 1 212 | 213 | [xallint, yallint] = polyjoin(xint, yint); 214 | if isempty(xallint) 215 | xout = x1; 216 | yout = y1; 217 | else 218 | [xout, yout] = polybool('xor', x1, y1, xallint, yallint); 219 | end 220 | 221 | if isempty(xout) 222 | indout = []; 223 | else 224 | indout = ipoly; 225 | end 226 | 227 | xtemp = [xint xxor {xout}]; 228 | ytemp = [yint yxor {yout}]; 229 | indtemp = [indint indxor {indout}]; 230 | 231 | isbad = cellfun(@(a,b,c) isempty(a) & isempty(b) & isempty(c), xtemp, ytemp, indtemp); 232 | xnew = xtemp(~isbad); 233 | ynew = ytemp(~isbad); 234 | indices = indtemp(~isbad); 235 | end 236 | 237 | case 'gpcmex' % The riskier way, going straight to gpcmex 238 | 239 | % Convert all polygons to GPC-style structures 240 | 241 | p = cell(length(x),1); 242 | for ii = 1:length(x) 243 | [xtmp, ytmp] = polysplit(x{ii},y{ii}); 244 | p{ii} = struct('x', xtmp, 'y', ytmp, 'ishole', num2cell(~ispolycw(xtmp, ytmp))); 245 | end 246 | 247 | % Start with first polygon 248 | 249 | pnew = p(1); 250 | indices = {1}; 251 | 252 | isemp = @(p) isempty([p.x]); 253 | 254 | for ipoly = 2:length(x) 255 | 256 | p1 = p{ipoly}; 257 | for icomp = 1:length(pnew) 258 | 259 | p2 = pnew{icomp}; 260 | 261 | % Intersecting 262 | 263 | pint{icomp} = gpcmex('int', p1, p2); 264 | noint = isemp(pint{icomp}); 265 | 266 | % Only in 2 267 | 268 | if noint 269 | pxor{icomp} = p2; 270 | else 271 | pxor{icomp} = gpcmex('xor', p2, pint{icomp}); 272 | end 273 | 274 | noxor = isemp(pxor{icomp}); 275 | 276 | % Indices 277 | 278 | if noint 279 | indint{icomp} = []; 280 | else 281 | indint{icomp} = [indices{icomp} ipoly]; 282 | end 283 | 284 | if noxor 285 | indxor{icomp} = []; 286 | else 287 | indxor{icomp} = indices{icomp}; 288 | end 289 | 290 | end 291 | 292 | % Only in 1 293 | 294 | pallint = cat(1, pint{:}); 295 | if isemp(pallint) 296 | pout = p1; 297 | else 298 | pout = gpcmex('xor', p1, pallint); 299 | end 300 | 301 | if isemp(pout) 302 | indout = []; 303 | else 304 | indout = ipoly; 305 | end 306 | 307 | ptemp = [pint pxor {pout}]; 308 | indtemp = [indint indxor {indout}]; 309 | 310 | isbad = cellfun(@(a,b) isempty(a) & isempty(b), ptemp, indtemp); 311 | pnew = ptemp(~isbad); 312 | indices = indtemp(~isbad); 313 | 314 | end 315 | 316 | % Convert back to cell arrays 317 | 318 | [xnew, ynew] = cellfun(vectorsFromGPC, pnew, 'uni', 0); 319 | xnew = xnew(:); 320 | ynew = ynew(:); 321 | case 'polyshape' 322 | 323 | % Convert all polygons to GPC-style structures 324 | 325 | W = warning('off'); % turn off duplicate vertices warning 326 | try 327 | p = polyshape; 328 | for ii = length(x):-1:1 329 | p(ii) = polyshape(x{ii}, y{ii}); 330 | end 331 | warning(W); 332 | catch ME 333 | warning(W); 334 | rethrow(ME); 335 | end 336 | 337 | % Start with first polygon 338 | 339 | pnew = p(1); 340 | indices = {1}; 341 | 342 | isemp = @(p) isempty([p.Vertices]); 343 | 344 | pint = polyshape; 345 | pxor = polyshape; 346 | for ipoly = 2:length(x) 347 | 348 | p1 = p(ipoly); 349 | for icomp = 1:length(pnew) 350 | 351 | p2 = pnew(icomp); 352 | 353 | % Intersecting 354 | 355 | pint(icomp) = intersect(p1, p2); 356 | noint = isemp(pint(icomp)); 357 | 358 | % Only in 2 359 | 360 | if noint 361 | pxor(icomp) = p2; 362 | else 363 | pxor(icomp) = xor(p2, pint(icomp)); 364 | end 365 | 366 | noxor = isemp(pxor(icomp)); 367 | 368 | % Indices 369 | 370 | if noint 371 | indint{icomp} = []; 372 | else 373 | indint{icomp} = [indices{icomp} ipoly]; 374 | end 375 | 376 | if noxor 377 | indxor{icomp} = []; 378 | else 379 | indxor{icomp} = indices{icomp}; 380 | end 381 | 382 | end 383 | 384 | % Only in 1 385 | 386 | 387 | pallint = pint(1); 388 | for ii = 2:length(pint) 389 | pallint = union(pallint, pint(ii)); 390 | end 391 | if isemp(pallint) 392 | pout = p1; 393 | else 394 | pout = xor(p1, pallint); 395 | end 396 | 397 | if isemp(pout) 398 | indout = []; 399 | else 400 | indout = ipoly; 401 | end 402 | 403 | ptemp = [pint pxor pout]; 404 | indtemp = [indint indxor {indout}]; 405 | 406 | isbad = cellfun(@(a,b) isempty(a.Vertices) & isempty(b), num2cell(ptemp), indtemp); 407 | pnew = ptemp(~isbad); 408 | indices = indtemp(~isbad); 409 | 410 | end 411 | 412 | % Convert back to cell arrays 413 | 414 | [xnew, ynew] = deal(cell(length(pnew),1)); 415 | for ii = 1:length(pnew) 416 | [xnew{ii}, ynew{ii}] = boundary(pnew(ii)); 417 | end 418 | % [xnew, ynew] = cellfun(vectorsFromGPC, pnew, 'uni', 0); 419 | % xnew = xnew(:); 420 | % ynew = ynew(:); 421 | 422 | end 423 | 424 | 425 | %------------------------------ 426 | % Subfunction: stripped down 427 | % polybool 428 | %------------------------------ 429 | 430 | % Note: This speeds things up just a bit, but much more speed could be 431 | % gained by skipping vectors[To/From]GPC entirely, because it's the 432 | % clockwise checks in there that eat up a lot of unecessary time. 433 | 434 | function [x3, y3] = polyboolfast(op, x1, y1, x2, y2, vectorsToGPC, vectorsFromGPC, gpcmex) 435 | 436 | % Handle empties 437 | 438 | if all(isnan(x1)) && ~isempty(x1) 439 | x1(1:end) = []; 440 | y1(1:end) = []; 441 | end 442 | 443 | if all(isnan(x2)) && ~isempty(x2) 444 | x2(1:end) = []; 445 | y2(1:end) = []; 446 | end 447 | 448 | if isempty(x2) 449 | if strcmp(op,'int') 450 | % Intersection is empty, but preserve shape 451 | % by using x2 and y2 rather than []. 452 | x3 = x2; 453 | y3 = y2; 454 | else 455 | % Union, exclusive or, or difference with 456 | % empty leaves x1 and y1 unaltered. 457 | x3 = x1; 458 | y3 = y1; 459 | end 460 | emptyInput = true; 461 | elseif isempty(x1) 462 | if any(strcmp(op,{'int','diff'})) 463 | % Intersection or difference is empty, but preserve 464 | % shape by using x1 and y1 rather than []. 465 | x3 = x1; 466 | y3 = y1; 467 | else 468 | % Union or exclusive or with empty leaves x2 and y2 unaltered. 469 | x3 = x2; 470 | y3 = y2; 471 | end 472 | emptyInput = true; 473 | else 474 | x3 = []; 475 | y3 = []; 476 | emptyInput = false; 477 | end 478 | 479 | % Calculate 480 | 481 | if ~emptyInput 482 | p1 = vectorsToGPC(x1, y1); 483 | p2 = vectorsToGPC(x2, y2); 484 | if (length(p1)==1 && p1.ishole) || (length(p1)>1 && (p1(1).ishole || ~all([p1(2:end).ishole]))) || ... 485 | (length(p2)==1 && p2.ishole) || (length(p1)>1 && (p2(1).ishole || ~all([p1(2:end).ishole]))) 486 | blah 487 | end 488 | p3 = gpcmex(op, p1, p2); 489 | [x3, y3] = vectorsFromGPC(p3); 490 | end 491 | 492 | 493 | 494 | 495 | -------------------------------------------------------------------------------- /pcolorbar/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | ehthumbs.db 37 | Thumbs.db -------------------------------------------------------------------------------- /pcolorbar/pcolorbar.m: -------------------------------------------------------------------------------- 1 | function h = pcolorbar(clev, cmap, varargin) 2 | %PCOLORBAR Create a pseudo-colorbar with static color blocks 3 | % 4 | % h = pcolorbar(clev, cmap, ...) 5 | % 6 | % This function creates an axis that mimics a colorbar. However, rather 7 | % than using data from the axis' actual colormap, it plots the specific 8 | % color intervals and colors specified by the user. 9 | % 10 | % Input variables: 11 | % 12 | % clev: vector of length n defining the edges of the color blocks 13 | % 14 | % cmap: n-1 x 3 colormap array, colors corresponding to the 15 | % intervals between each level in the clev array 16 | % 17 | % Optional input variables (passed as parameter/value pairs): 18 | % 19 | % peer: axis to attach colorbar to [gca] 20 | % 21 | % location: location of colorbar (same options as regular colorbar 22 | % command) ['east'] 23 | % 24 | % even: logical scalar, if true, create evenly-sized color blocks 25 | % regardless of the values assigned. Ticks labels will 26 | % reflect the values in clev. [false] 27 | % 28 | % tkint: interval used to label ticks, with tick 1:tkint:end 29 | % receiving a label. 30 | % 31 | % cmapt: n-1 x 3 colormap array corresponding to the top/right of 32 | % each color block. If a row in cmapt is different than the 33 | % corresponding row in cmap, then the block will show a 34 | % linear gradient between the two colors. By default, 35 | % solid-colored blocks are drawn. 36 | % 37 | % Output variables: 38 | % 39 | % h: structure with the following fields: 40 | % 41 | % cb: handle to the non-visible, real colorbar. The 42 | % pseudocolorbar will shadow this one for position. 43 | % (Note: if you want to change the position of the 44 | % pseudocolorbar, change this handle's position 45 | % property). 46 | % 47 | % ax: handle to pseudocolorbar axis 48 | % 49 | % p: handle to pseudocolorbar patch object 50 | % 51 | % el: event listener that links h.ax position to h.cb 52 | % position. 53 | 54 | % Copyright 2018 Kelly Kearney 55 | 56 | % Parse input 57 | 58 | p = inputParser; 59 | p.addParameter('peer', gca, @(x) validateattributes(x, {}, {'scalar'})); 60 | p.addParameter('location', 'east', @(x) validateattributes(x, {'char','string'}, {'scalartext'})); 61 | p.addParameter('even', false, @(x) validateattributes(x, {'logical'}, {'scalar'})); 62 | p.addParameter('tkint', 1, @(x) validateattributes(x, {'numeric'}, {'integer'})); 63 | p.addParameter('cmapt', zeros(0,3), @(x) validateattributes(x, {'numeric'}, {'ncols',3,'>=',0,'<=',1})); 64 | 65 | p.parse(varargin{:}); 66 | 67 | Opt = p.Results; 68 | 69 | validateattributes(cmap, {'numeric'}, {'ncols',3,'>=',0,'<=',1}, 'pcolorbar', 'cmap'); 70 | validateattributes(clev, {'numeric'}, {'vector', 'increasing'}, 'pcolorbar', 'clev'); 71 | 72 | nblock = length(clev) - 1; 73 | 74 | if isempty(Opt.cmapt) 75 | Opt.cmapt = cmap; 76 | end 77 | 78 | if ~isequal(nblock, size(cmap,1), size(Opt.cmapt,1)) 79 | error('Colormap array(s) should include one less color than clev array'); 80 | end 81 | 82 | clev = clev(:); 83 | 84 | % Create a real colorbar, used for position info 85 | 86 | h.cb = colorbar('peer', Opt.peer, 'location', Opt.location); 87 | 88 | loc = get(h.cb, 'Location'); % Easier than lower/upper 89 | pos = get(h.cb, 'position'); 90 | 91 | % Calculate y-values for each color interval, and labels to go along 92 | 93 | if Opt.even 94 | tk = linspace(0,1,nblock+1)'; 95 | y1 = tk(1:end-1); 96 | y2 = tk(2:end); 97 | else 98 | y1 = clev(1:end-1); 99 | y2 = clev(2:end); 100 | tk = clev; 101 | end 102 | tklbl = strtrim(cellstr(num2str(clev))); 103 | 104 | islbl = ismember(1:length(tk), 1:Opt.tkint:length(tk)); 105 | [tklbl{~islbl}] = deal(' '); 106 | 107 | % Coordinates and values for patch vertices 108 | 109 | ypatch = [y1 y2 y2 y1 y1]'; 110 | xpatch = repmat([0 0 1 1 0], size(ypatch,2), 1)'; 111 | 112 | cpatch = cat(3, cmap, Opt.cmapt, Opt.cmapt, cmap, cmap); 113 | cpatch = permute(cpatch, [3 1 2]); 114 | 115 | % Create pseudo-colorbar 116 | 117 | currax = get(gcf, 'CurrentAxes'); 118 | h.ax = axes('position', pos, 'box', 'on'); 119 | 120 | switch lower(loc) 121 | case {'east', 'west', 'eastoutside', 'westoutside'} 122 | h.p = patch(xpatch, ypatch, cpatch); 123 | set(h.ax, 'ytick', tk, 'yticklabel', tklbl, 'ylim', [min(tk) max(tk)], ... 124 | 'xlim', [0 1], 'xtick', []); 125 | otherwise 126 | h.p = patch(ypatch, xpatch, cpatch); 127 | set(h.ax, 'xtick', tk, 'xticklabel', tklbl, 'xlim', [min(tk) max(tk)], ... 128 | 'ylim', [0 1], 'ytick', []); 129 | end 130 | 131 | switch lower(loc) 132 | case {'eastoutside', 'west'} 133 | set(h.ax, 'yaxislocation', 'right'); 134 | case {'south', 'northoutside'} 135 | set(h.ax, 'xaxislocation', 'top'); 136 | end 137 | 138 | set(h.ax, 'layer', 'top'); 139 | 140 | set(h.p, 'edgecolor', 'none'); 141 | set(h.cb, 'visible', 'off'); 142 | 143 | % Create listener so axis size updates with colorbar 144 | 145 | try 146 | h.el = addlistener(h.cb, 'MarkedClean', @(he,ed) set(h.ax, 'position', get(he, 'position'))); 147 | catch 148 | warning('Unable to add colorbar resizing listener'); 149 | end -------------------------------------------------------------------------------- /readmeExtras/README_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakearney/contourfcmap-pkg/15e090ce62713ab78e52f62a05ed0b3b98ad2224/readmeExtras/README_01.png -------------------------------------------------------------------------------- /readmeExtras/README_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakearney/contourfcmap-pkg/15e090ce62713ab78e52f62a05ed0b3b98ad2224/readmeExtras/README_02.png -------------------------------------------------------------------------------- /readmeExtras/README_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakearney/contourfcmap-pkg/15e090ce62713ab78e52f62a05ed0b3b98ad2224/readmeExtras/README_03.png -------------------------------------------------------------------------------- /readmeExtras/README_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakearney/contourfcmap-pkg/15e090ce62713ab78e52f62a05ed0b3b98ad2224/readmeExtras/README_04.png -------------------------------------------------------------------------------- /readmeExtras/README_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakearney/contourfcmap-pkg/15e090ce62713ab78e52f62a05ed0b3b98ad2224/readmeExtras/README_05.png -------------------------------------------------------------------------------- /readmeExtras/README_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakearney/contourfcmap-pkg/15e090ce62713ab78e52f62a05ed0b3b98ad2224/readmeExtras/README_06.png -------------------------------------------------------------------------------- /readmeExtras/README_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakearney/contourfcmap-pkg/15e090ce62713ab78e52f62a05ed0b3b98ad2224/readmeExtras/README_07.png -------------------------------------------------------------------------------- /readmeExtras/README_08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakearney/contourfcmap-pkg/15e090ce62713ab78e52f62a05ed0b3b98ad2224/readmeExtras/README_08.png -------------------------------------------------------------------------------- /readmeExtras/README_09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakearney/contourfcmap-pkg/15e090ce62713ab78e52f62a05ed0b3b98ad2224/readmeExtras/README_09.png -------------------------------------------------------------------------------- /readmeExtras/README_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakearney/contourfcmap-pkg/15e090ce62713ab78e52f62a05ed0b3b98ad2224/readmeExtras/README_10.png -------------------------------------------------------------------------------- /readmeExtras/README_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakearney/contourfcmap-pkg/15e090ce62713ab78e52f62a05ed0b3b98ad2224/readmeExtras/README_11.png --------------------------------------------------------------------------------