Authored by Jean-Luc Shaw
0 parents
Exists in

### Initial commit.

Showing 30 changed files with 2128 additions and 0 deletions
geo/dm2deci.m 0 → 100644

 1 +++ a/geo/dm2deci.m ... ... @@ -0,0 +1,23 @@ 1 +function [lond,latd] = dm2deci(londeg,lonmin,lonsec,EW,latdeg,latmin,latsec,NS) 2 + 3 +% 4 +% This function converts coordinates from deg/min/sec to decimal 5 +% 6 +% [lond,latd] = dm2deci(londeg,lonmin,lonsec,EW,latdeg,latmin,latsec,NS) 7 +% 8 +% Enter numbers for longitude degrees, minutes and seconds, and a string for cardinal direction ('E' or 'W') 9 +% followed by 10 +% numbers for latitude degrees, minutes and seconds, and a string for cardinal direction ('N' or 'S') 11 +% 12 +% Written by Jean-Luc Shaw 13 +% 14 + 15 +if(strmatch(NS,'N')) ; lat_sign = 1. ; end 16 +if(strmatch(NS,'S')) ; lat_sign = -1. ; end 17 +if(strmatch(EW,'E')) ; lon_sign = 1. ; end 18 +if(strmatch(EW,'W')) ; lon_sign = -1. ; end 19 + 20 +lond = lon_sign*(londeg+lonmin./60+lonsec./3600) ; 21 +latd = lat_sign*(latdeg+latmin./60+latsec./3600) ; 22 + 23 +end ... ...
geo/globeGrid.m 0 → 100644

 1 +++ a/geo/globeGrid.m ... ... @@ -0,0 +1,44 @@ 1 +function [LNG,LAT] = globeGrid(lims,sz) 2 +% Synthax : [LNG,LAT] = globeGrid(lims,sz) 3 +% 4 +% Takes as input longitude and latitude limits 'lims', in format : 5 +% 6 +% lims = [lon_min lon_max lat_min lat_max] 7 +% 8 +% and a grid size 'sz' in kilometers, and generates a grid uniform in 9 +% kilometer space, with the center of the x dimension aligned with the 10 +% center of the area specified by 'lims'. This is mostly designed for use 11 +% with small grids (~50-100km^2) where this representation looks almost 12 +% regular in coordinate space and kind of "looks nice"... at least to me it 13 +% does. 14 +% 15 +% 16 + 17 +% EARTH RADIUS 18 +R = 6371 ; 19 + 20 +% convert to km 21 +lng = [lims(1); lims(1); lims(2); lims(2)] ; 22 +lat = [lims(3); lims(4); lims(3); lims(4)] ; 23 +avl = mean(lng) ; 24 +lng = lng - avl ; % move to prime meridian 25 + 26 +% SINUSOIDAL PROJECTION 27 +y = ( lat*R*pi / 180 ) ; 28 +x = ( cosd(lat).*lng*R*pi / 180 ) ; 29 + 30 +avy = mean(y) ; 31 +avx = mean(x) ; 32 + 33 +% MAKE GRID VECTORS AROUND CENTER IN KM SPACE 34 +xg = [flipud( [avx:-sz:min(x)]' ) ; [avx+sz:sz:max(x)]' ] ; 35 +yg = [flipud( [avy:-sz:min(y)]' ) ; [avy+sz:sz:max(y)]' ] ; 36 + 37 +% GENERATE UNIFORM GRID IN KM SPACE 38 +[X,Y] = meshgrid(xg,yg) ; 39 + 40 +% CONVERT BACK TO GEOGRAPHICAL COORDINATES 41 +LAT = 180*Y./(R*pi) ; 42 +LNG = 180*X./(R*pi*cosd(LAT)) + avl ; % moved back to average meridian 43 + 44 +end 0 45 \ No newline at end of file ... ...
geo/ll2area.m 0 → 100644

 1 +++ a/geo/ll2area.m ... ... @@ -0,0 +1,59 @@ 1 +function A = ll2area(lng,lat,unit) 2 +% Synthax : A = ll2area(lng,lat,unit) 3 +% 4 +% Takes as input the geographical coordinate vectors for longitude 'lng' 5 +% and latitude 'lat' in column format and returns the area inside the 6 +% non-intersecting polygon they form as ouput 'A' in units 'unit'. 7 +% recognized unit options are 'm' for square meters and 'km' for square 8 +% kilometers. 9 +% 10 +% BASED ON: 11 +% 12 +% https://www.periscopedata.com/blog/polygon-area-from-latitude-and-longitude-using-sql 13 +% 14 +% and 15 +% 16 +% http://geomalgorithms.com/a01-_area.html 17 + 18 +% PARAMETERS 19 +switch unit 20 + case 'km' 21 + R = 6371 ; % earth radius (km) 22 + case 'm' 23 + R = 6371000 ; % earth radius (m) 24 +end 25 + 26 +% CONVERT TO XY 27 +y = ( lat*R*pi / 180 ) ; 28 +x = ( cosd(lat).*lng*R*pi / 180 ) ; 29 +avx = mean(x) ; 30 +avy = mean(y) ; 31 +normx = x - avx ; 32 +normy = y - avy ; 33 + 34 +% GET ANGLE FOR EVERY VERTEX (0-360) and convert 35 +ang = atan2d(normy,normx); % + (-1*normx./abs(normx))*90 + 180 ; 36 +ang( ang < 0 ) = ang( ang < 0 ) + 360 ; 37 +ang = ang - 90; 38 +ang = 360 - ang ; 39 +ang(ang < 0) = ang(ang < 0) + 360 ; 40 +ang = mod(ang,360) ; 41 + 42 +% SORT TO AVOID SELF INTERSECTION 43 +[~,IA] = sort(ang,'descend') ; 44 +x = x(IA) ; 45 +y = y(IA) ; 46 + 47 +% APPEND FOR WRAPPING 48 +x1 = x(1); xn = x(end) ; 49 +y1 = y(1); yn = y(end) ; 50 +x = [xn; x; x1] ; 51 +y = [yn; y; y1] ; 52 + 53 +% COMPUTE THE SUM 54 +A = 0 ; 55 +for ii = 2:numel(x)-1 56 + A = A + 0.5*( x(ii).*( y(ii+1) - y(ii-1) ) ) ; 57 +end 58 + 59 +end ... ...
geo/llt2spd.m 0 → 100644

 1 +++ a/geo/llt2spd.m ... ... @@ -0,0 +1,49 @@ 1 +function [u,v,spd,head] = llt2spd(lon,lat,time) 2 + 3 +% Synthax : [u,v,spd,head] = llt2spd(lon,lat,time) 4 +% 5 +% Returns the speed 'spd' and it's eastward/northward components 'u' and 6 +% 'v' in (m/s), and the heading 'head' in degrees with 0 as east increasing 7 +% towards the north (90 degrees). 8 +% 9 +% Values are calculated from a latitude/longitude time series with 10 +% coordinates in decimal degrees and time in days. 11 +% 12 +% Values are estimated between longitude/latitude coordinates and then 13 +% interpolated linearly to the input 'time' vector. Begining and end 14 +% values are extrapolated using the "pchip" method. 15 + 16 +%% make everything column vectors 17 +if ~iscolumn(lon) ; lon = lon' ; end 18 +if ~iscolumn(lat) ; lat = lat' ; end 19 +if ~iscolumn(time) ; time = time' ; end 20 + 21 +%% make differential vectors 22 +dlon = diff(lon) ; 23 +dlat = diff(lat) ; 24 +dt = diff(time) ; 25 +dl = m_lldist(lon,lat)*1000 ; % (m) 26 + 27 +%% calculate norm of speed 28 +spd = dl./(dt*24*60*60) ; 29 + 30 +%% calculate heading 31 +head = range_pm180_2_360(atan2d(dlat,dlon)) ; 32 + 33 +%% decompose into eastward/northward 34 +u = spd.*cosd(head) ; 35 +v = spd.*sind(head) ; 36 + 37 +%% make the time vector 38 +t = time(1:end-1)+0.5*dt(1); 39 + 40 +%% interpolate to the input time grid 41 +u = interp1(t,u,time,'linear','extrap') ; 42 +v = interp1(t,v,time,'linear','extrap') ; 43 +spd = interp1(t,spd,time) ; 44 +spd(1) = sqrt(u(1).^2+v(1).^2); 45 +spd(end) = sqrt(u(end).^2+v(end).^2); 46 +head = interp1(t,head,time) ; 47 + 48 + 49 +end ... ...
geo/range_360_2_pm180.m 0 → 100644

 1 +++ a/geo/range_360_2_pm180.m ... ... @@ -0,0 +1,8 @@ 1 +function output = range_360_2_pm180(input) 2 + 3 +I = find(input > 180) ; 4 +input(I) = input(I) - 360 ; 5 + 6 +output = input ; 7 + 8 +end ... ...
geo/range_pm180_2_360.m 0 → 100644

 1 +++ a/geo/range_pm180_2_360.m ... ... @@ -0,0 +1,8 @@ 1 +function output = range_pm180_2_360(input) 2 + 3 +I = find(input < 0) ; 4 +input(I) = input(I) + 360 ; 5 + 6 +output = input ; 7 + 8 +end ... ...

 1 +++ a/geo/theta2heading.m ... ... @@ -0,0 +1,15 @@ 1 + 2 +function output = theta2heading(input) 3 +% Synthax : output = theta2heading(input) 4 +% 5 +% Takes as input angle vector in cartesian coordinates running counter- 6 +% clockwise from 0 (east) to 360 degrees and transforms it into a heading 7 +% vector running clockwise from 0 to the north to 360 degrees. 8 +% 9 + 10 +input = input - 90; 11 +input = 360 - input ; 12 +input(input < 0) = input(input < 0) + 360 ; 13 +output = mod(input,360) ; 14 + 15 +end ... ...
math/SEplot.m 0 → 100644

 1 +++ a/math/SEplot.m ... ... @@ -0,0 +1,36 @@ 1 +function [bins,bs,m,M]=SEplot(dist) 2 +% Synthax : [bins,bs,m,M]=SEplot(dist) 3 +% 4 +% Produces a histogram plot of distribution 'dist' with a color code 5 +% identifying which portions of the distribution correspond to +-1 standard 6 +% error, +-2 standard error, +-3 standard error, and beyond. Limits are 7 +% included to the categories and standard error is calculated with the 8 +% built in matlab std function 9 + 10 +sigma = std(dist,'omitnan') ; 11 +mdist = mean(dist,'omitnan') ; 12 + 13 +M = max(dist) ; 14 +m = min(dist) ; 15 + 16 +bs = sigma./20 ; 17 +bins = sort([mdist:-bs:m mdist+bs:bs:M]); 18 + 19 +figure ; 20 +i = find(dist <= mdist + 1*sigma & dist >= mdist - 1.*sigma) ; 21 +histogram(dist(i),bins,'facecolor','b') ; 22 + 23 +hold on 24 + 25 +i = find(dist <= mdist + 2*sigma & dist > mdist + 1.*sigma | ... 26 + dist < mdist - 1*sigma & dist >= mdist - 2.*sigma) ; 27 +histogram(dist(i),bins,'facecolor','g') ; 28 + 29 +i = find(dist <= mdist + 3*sigma & dist > mdist + 2.*sigma | ... 30 + dist < mdist - 2*sigma & dist >= mdist - 3.*sigma) ; 31 +histogram(dist(i),bins,'facecolor','r') ; 32 + 33 +i = find(dist <= mdist - 3*sigma | dist >= mdist + 3.*sigma) ; 34 +histogram(dist(i),bins,'facecolor','c') ; 35 + 36 +end ... ...
math/SEtrunc.m 0 → 100644

 1 +++ a/math/SEtrunc.m ... ... @@ -0,0 +1,19 @@ 1 +function [dist,I] = SEtrunc(dist,thres) 2 +% Synthax : [dist,I] = SEtrunc(dist,thres) 3 +% 4 +% Replaces with nan the values in dist which are outside the range 5 +% 6 +% +-'thres'*std('dist') 7 +% 8 +% where std is matlab built in function. Limits of the range are excluded. 9 +% also returns the index values 'I' of the values nan-ed. 10 + 11 +sigma = std(dist,'omitnan') ; 12 +mdist = mean(dist,'omitnan') ; 13 + 14 +I = find(dist < mdist - thres.*sigma | dist > mdist + thres.*sigma) ; 15 +dist(I) = nan ; 16 + 17 +end 18 + 19 + ... ...
math/jlpca.m 0 → 100644

 1 +++ a/math/jlpca.m ... ... @@ -0,0 +1,40 @@ 1 +function [vals,vecs,stds] = jlpca(X) 2 +% Synthax : [vals,vecs,stds] = jlpca(X) 3 +% 4 +% Performs principal component analysis on input matrix 'X' . Input matrix 5 +% must be in a form where columns are different features of the data set 6 +% and rows are repetitions of the same measurement. 7 +% 8 +% Also returns the standard deviation of every feature projected in the space 9 +% of the eigenvectors found by the PCA. 10 +% 11 + 12 + 13 +[m,n] = size(X) ; 14 + 15 + 16 +%% De-trend 17 +F = repmat(nan,size(X)) ; 18 +for ii = 1:n 19 + F(:,ii) = X(:,ii) - mean(X(:,ii),'omitnan') ; 20 +end 21 + 22 +%% Build covariance matrix 23 +R = F'*F ; 24 + 25 +%% Find eigenvalues 26 +[C,L] = eig(R) ; 27 + 28 +%% sort and normalise 29 +vals = sort(diag(L),'descend') ; 30 + 31 +% eigenvectors in growing importance 32 +vecs = fliplr(C) ; 33 + 34 +%% Project in the space of the eigenvectors 35 +Fprime = inv(vecs)*F' ; 36 + 37 +%% Get the standard error in each "eigendimension" 38 +stds = std(Fprime,[],2) ; 39 + 40 +end ... ...
math/nanmean.m 0 → 100644

 1 +++ a/math/nanmean.m ... ... @@ -0,0 +1,10 @@ 1 +function output = nanmean(input,varargin) 2 + 3 +% 4 +% Matlab is super cheap for not including this 5 +% in the student license. Seriously. 6 +% 7 + 8 +I = find(isfinite(input)) ; 9 +output = mean(input(I),varargin{:}) ; 10 +end ... ...
math/nanmedian.m 0 → 100644

 1 +++ a/math/nanmedian.m ... ... @@ -0,0 +1,6 @@ 1 +function out = nanmedian(in) 2 + 3 +I = find(isfinite(in)) ; 4 +out = median(in(I)) ; 5 + 6 +end ... ...
math/nlinfit.m 0 → 100644

 1 +++ a/math/nlinfit.m ... ... @@ -0,0 +1,33 @@ 1 +function [yfit,A] = nlinfit(rhs,x,y,pars,varargin) 2 +% 3 +% Synthax : [yfit,A] = nlinfit(rhs,x,y,pars,cst1,cst2,...,cstn) 4 +% 5 +% An idea found online and remodeled to make it more modular about how to 6 +% fminsearch.m to perform non-linear, multiple parameter function fitting. 7 +% Variables 'x' and 'y' are the input data, i.e., y = y(x) and must be 8 +% vectors. Variable 'rhs' is a function handle refering to te mathematical 9 +% form of the fitted function. For example, to fit a 1/6 power law, define: 10 +% 11 +% rhs = @(a,x) a(1)*x.^(1/6) + a(2) ; 12 +% 13 +% where 'a' is a vector formed of the parameters to fit and 'x' is the 14 +% input independent variable. If variable 'pars' is entered as an integer 15 +% it is interpreted as the number of parameters to fit, and sets the first 16 +% guess for all parameters to zero. If 'pars' is entered as a vector, then 17 +% it is interpreted as the first guess for all parameters. 18 +% 19 +% The function returns the fitted values at 'x' as well as the optimized 20 +% parameter vector 'A'. 21 + 22 +% Is pars first guess for parameters or number of parameters 23 +sz = size(pars) ; 24 +if sz(1) == 1 & sz(2) == 1 25 + start = zeros([pars 1]) ; 26 +elseif sz(1) == 1 & sz(2) > 1 | sz(2) == 1 & sz(1) > 1 27 + start = pars ; 28 +end 29 + 30 +% Minimize parameters 31 + A = fminsearch(@(a)sum( (y - rhs(a,x,varargin{:})).^2 ),start) ; 32 + yfit = rhs(A,x,varargin{:}) ; 33 +end ... ...
math/smooth2D.m 0 → 100644

math/unifrnd.m 0 → 100644

 1 +++ a/math/unifrnd.m ... ... @@ -0,0 +1,17 @@ 1 +function output = unifrnd(b_min,b_max,d_len,m,n) 2 + 3 +% Synthax : output = unifrnd(b_min,b_max,d_len,m,n) 4 +% 5 +% Imitates the unifrnd function from the statistics toolbox. Generates a 6 +% 'm' by 'n' matrix of values chosen from a uniform distribution of minimum 7 +% 'b_min' and maximum 'b_max' . The parameter 'd_len' controls the number 8 +% of elements in the uniform distribution the 'output' values are chosen 9 +% from. 10 +% 11 + 12 +dist = linspace(b_min,b_max,d_len)' ; 13 +I = randi([1 d_len],[m n]) ; 14 + 15 +output = dist(I) ; 16 + 17 +end 0 18 \ No newline at end of file ... ...
math/vvangle.m 0 → 100644

 1 +++ a/math/vvangle.m ... ... @@ -0,0 +1,21 @@ 1 +function theta = vvangle(v1,v2) 2 +% Synthax : theta = vvangle(v1,v2) 3 +% 4 +% Get the absolute angle between two line vectors, or between the vectors 5 +% described by the lines v1 and v2. 6 +% 7 + 8 +%% make sure the sizes are compatible 9 +if ~( isequal(size(v1),size(v2)) || isvector(v1) && isvector(v2) && numel(v1) == numel(v2) ) 10 + disp(' Error : v1 and v2 have different dimensions!') ; 11 + return ; 12 +end 13 + 14 +%% get the vector norms 15 +nv1 = sum(abs(v1).^2,2).^(1/2) ; 16 +nv2 = sum(abs(v2).^2,2).^(1/2) ; 17 + 18 +%% calculate the angle 19 +theta = acosd( ( dot(v1,v2,2) ) ./ (nv1.*nv2) ) ; 20 + 21 +end 0 22 \ No newline at end of file ... ...