Code covered by the BSD License  

Highlights from
Plot earth

image thumbnail
from Plot earth by Bruno Luong
Plot earth in 3D

plotearth(varargin)
function h = plotearth(varargin)
% function h = plotearth(Property1, Value1, ...)
% Plot the earth map on sphere
%
% Propert/Value pairs, Properties are strings:
% 'MapType', string map among the following
%   - low resolution: 'texture' (default), 'relief'
%   - hight resolution: 'nasa', 'avhrr', 'usgs' (large memory required)
%   - NEO maps, hight resolution: 'bluemarble' 'topo'
% 'NEOMap': filename of the maps from NEO/ESA website (see below)
% 'SampleStep', integer: 1 -> highest resolution from the orginal map
%                      larger -> lower resolution
% 'FullColor', boolean: true -> RGB (required more memory)
%                       false -> indexed color (degraded color, default)
% 'NColors', integer: number of color used for indexed color map
%                     256 by default. Ignored when 'FullColor' is TRUE
% 'ViewPoint', 1 x 2 array, azimuthal/elevation angles (degree)
% 'Shape': 'spheroid' (default), or 'spherical'
% 'Plot2DMap', boolean: plot the resampling flat 2D map, false by default
% 'AxeHandle', handle of the axes
%
% Some plots is based on the maps available from
%   http://www.progonos.com/furuti/MapProj/CartIndex/cartIndex.html
%
% Few other are based from Nasa Earth Observation (NEO) 
%   http://neo.sci.gsfc.nasa.gov/Search.html
% Instruction to download NEO map from this website:
%   - Select your map
%   - Select option <Full>, <Color>, <PNG> on the right panel
%   - Click on <GET IMAGE> button and save (right mouse click) 'mymap.png'
%     on the same directory, filename 'Population.png'
%   - Under Matlab
%       >> plotearth('neomap', 'Population', ...)
%
% See the respective websites for copyright information
%
% A collection of maps is compiled and can be downoaded in the link
%   http://www.mediafire.com/?m2mn1mgdngt
%
% OUTPUT: Return the handle of the AXE
%
% NOTES:
%   - Shape is spheroid by default, unless id SPHERICAL is specified 
%   - The earth center is located at (0,0,0)
%   - Greenwitch meridian is aligned with the plane y=0
%   - Units are normalized by the eath equatorial radius of 6378.1370 km
%
% See also: cubedsphere, mercator
%
% Author: Bruno Luong <brunoluong@yahoo.com>
% History:
%   17-Aug-2009, original
%   18-Aug-2009, better use of memory
%                correct BUG for color-indexing
%                add 'NColors' property
%                indexing color is now default option
%                small gaps between cube-faces are now removed
%   19-Aug-2009, NEO/ESA map
%                correct bug overflow colormap in original file
%   20-Aug-2009, Read tiff/jpg maps
%                Spheroid shape
%   18-Oct-2011, fix Plot2DMap lowercase bug 

%% Parse inputs

% Default values
inputs = struct('maptype', 'texture', ...
                'neomap', '', ...
                'samplestep', NaN, ...
                'fullcolor', false, ...
                'verbose', true, ...
                'plot2dmap', false, ...
                'axehandle', NaN, ...
                'ncolors', 256, ...
                'viewpoint', [155 15],...
                'shape','spheroid');
            
validprop = fieldnames(inputs);
if mod(length(varargin),2)
    error('PLOTEARTH must be called with property/value pair');
end
% Parsing inputs
for k=1:2:length(varargin)
    property = varargin{k};
    if ~ischar(property)
        error('PLOTEARTH property must be a string');
    end
    property = lower(strtrim(property));
    idx = strmatch(property,validprop);
    if isempty(idx)
        error('PLOTEARTH not valid property ''%s''', property);
    end
    property = validprop{idx(1)};
    value = varargin{k+1};
    if ~isempty(value)
        inputs.(property) = value;
    end
end

% Displatch the inputs
maptype = inputs.maptype;
neomap = inputs.neomap;
samplestep = round(inputs.samplestep);
fullcolor = inputs.fullcolor;
verbose = inputs.verbose;
plot2dmap = inputs.plot2dmap;
axehandle = inputs.axehandle;
ncolors = inputs.ncolors;
viewpoint = inputs.viewpoint;
shape = inputs.shape;

%% Map selection
if ~isempty(neomap) % NEO map
    mapfname = neomap;
    % Add the png extension
    [pname maptype ext] = fileparts(mapfname); %#ok
    if isempty(ext)
        % Automatically look for image with following extension
        for ext = {'.png' '.PNG' ...
                   '.tif' '.TIF' '.tiff' '.TIFF' ...
                   '.jpg' '.JPG' '.jpeg' '.JPEG'}
            if exist([mapfname ext{1}],'file')
                mapfname = [mapfname ext{1}]; %#ok
                break;
            end
        end
    end
    projection = 'mercator';
    offsetx = 1; offsety = 1;
    nxy = NaN;
    mstep = 1;
else
    switch lower(maptype)
        case 'relief'
            mapfname = 'cbGn-s100.rel.png';
            projection = 'cube';
            offsetx = 2; offsety = 27; % upper/left corner
            nxy = 200;                 % size of each cube face, in pixel
            mstep = 1;                 % sampling step
        case 'texture'
            mapfname = 'cbGn-s100.tex.png';
            projection = 'cube';
            offsetx = 3; offsety = 27;
            nxy = 200;
            mstep = 1;
        case 'nasa'
            mapfname = 'cbGn_pof-bm.png';
            projection = 'cube';
            offsetx = 465; offsety = 215;
            nxy = 1524;
            mstep = 2;
        case 'avhrr'
            mapfname = 'cbGn_pof-z-25-pat.png';
            projection = 'cube';
            offsetx = 465; offsety = 215;
            nxy = 1524;
            mstep = 2;
        case 'usgs'
            mapfname = 'cbGn_poe-z20-tex.png';
            projection = 'cube';
            offsetx = 466; offsety = 210;
            nxy = 1525;
            mstep = 2;
        case 'bluemarble'
            mapfname = 'BlueMarble.png';
            projection = 'mercator';
            offsetx = 1; offsety = 1;
            nxy = NaN;
            mstep = 1;
        case 'topo'
            mapfname = 'Topo.png';
            projection = 'mercator';
            offsetx = 1; offsety = 1;
            nxy = NaN;
            mstep = 1;
        otherwise
            fprintf(['Maptype should be: ' ...
                '''texture'' ''relief'' ''nasa'' ''avhrr'' ''usgs''\n']);
            error('PLOTEARTH: unknown maptype <%s>', maptype);
    end
end

% Use default samplestep associated to each map 
if isnan(samplestep)
    samplestep = mstep;
end

    % nested function to display a text on command line window
    function display(varargin)
        if verbose
            fprintf(varargin{:});
        end
    end

%% Read the map
display('read the map\n')
display('\tmap: %s\n', mapfname)
[A map] = imread(mapfname);

try % sometime memory problem occurs
    
    % rotate 90 degree
    if strcmp(projection,'cube')
        A = permute(A,[2 1 3]);
        A = flipdim(A,1);
    end
    
    % Map to RGB
    if ~isempty(map)
        szA = size(A);
        if ~strcmp(class(A),'double') % must do that to avoid overflow!
            A = single(A); % this should cover 1/2-byte classes
        end
        A=map(A+1,:);
        A=reshape(A,[szA 3]);
        clear map
    end

    % Resampling RGB
    display('resampling image\n');
    [A m n] = resampling(A, offsetx, offsety, nxy, samplestep, ...
                         projection, @display);
    display('\tresolution = %d x %d x %d\n', m, n(1), n(2));
  
    % Indexed color
    if ~fullcolor && size(A,3)==3
        display('indexing color\n');
        if isempty(which('rgb2ind'))
            warning('RGB2IND:Missing', ...
                    'RRB2IND is missing (Image processing required)');
        else
            [A clmap] = rgb2ind(A,ncolors);
            display('\tnumber of colors = %d\n', size(clmap,1));
        end
    else
        display('full RGB color scheme\n');
    end
    % 3 (RGB) or 1 (indexed color)
    d3 = size(A,3);
    
    % Plot the flat 2D map
    if plot2dmap
        fig = figure();
        set(fig, 'Name', ['Map: ' maptype]);
        ax=axes('Parent',fig);
        image(A,'Parent',ax);
        if exist('clmap','var')
            colormap(ax,clmap);
        end
        axis(ax,'equal');
    end
    
    % Project on sphere
    switch projection
        case 'cube',
            display('compute cubed-sphere\n');
            [Vertices Faces FaceID] = cubedsphere(n(1), ...
                                               'equidistance', shape);
        case 'mercator',
            display('compute mercator-sphere\n');
            [Vertices Faces FaceID] = mercator(n, shape);            
    end
    
    %% Preparation
    display('preparation\n')
    
    xleft = 1 + ((1:m)-1)*n(2);
    xright = xleft + (n(2)-1);
    yupper = 1 + ((1:1)-1)*n(1);
    ylower = yupper + (n(1)-1);
    
    % Reshape the map as long vector of RGB
    xl = xleft(1:m);
    xr = xright(1:m);
    yu = yupper(repmat(1,m,1));
    yl = ylower(repmat(1,m,1));   
    
    % Save half of memory for full-color plot
    if d3==3
        A = single(A);
    end
    
    %% Plot the earth map on sphere
    display('plot earth\n')
    % Open axe
    if ~ishandle(axehandle)
        fig = figure();
        set(fig, 'Color','k', 'Name', ['Earth: ' maptype]); % black background
        
        pos = get(0,'screensize'); % full screen
        set(fig, 'Position', pos);
        ax = axes('Parent',fig,'pos',[0 0 1 1]);
    else
        ax = axehandle;
    end
    hold(ax,'on');
    
    % Mapping method
    if d3==3
        Mapping = 'direct';
    else
        Mapping = 'scaled';
    end
    
    for ID=1:m % loop on faces   
        
        % Get colors
        x = xl(ID):+1:xr(ID);
        y = yl(ID):-1:yu(ID);
        subs = {y x ':'};
        CData = reshape(A(subs{:}),[n(1),n(2),d3]);
        if ~strcmp(CData,'uint8')
            CData = double(CData); % CData must be double or uint8
        end
        
        % Get grid coordinates from vertices
        IDX = GridIdx(Faces, FaceID, ID, n, projection);
        x = reshape(Vertices(IDX,1),[n(1)+1 n(2)+1]);
        y = reshape(Vertices(IDX,2),[n(1)+1 n(2)+1]);
        z = reshape(Vertices(IDX,3),[n(1)+1 n(2)+1]);
        
        % Plot
        surf(ax,x,y,z,...
               'CData',CData,... 
               'EdgeColor','none',...
               'CDataMapping',Mapping);
    end % for loop
    if exist('clmap','var')
        colormap(ax,clmap);
    end    
    axis(ax,'off');
    axis(ax,'equal');
    view(ax,viewpoint);
    
    if nargout>=1
        % return the handle
        h = ax;
    end
    
catch %#ok % 'catch ME': syntax not compatible with V2006
    ME = lasterror(); %#ok
    if strcmp(ME.identifier,'MATLAB:nomem')
        fprintf('PLOTEARTH: Memory is full\n');
        fprintf('\tClose you figures (if any) and clear workspace\n');
        fprintf('\tIf it''s still fail, use a large SAMPLESTEP (=%d here)\n', samplestep);
    end
    rethrow(ME);
end

end % plotearth

%%
function [Acoarse m n] = resampling(A, x1, y1, nxy, samplestep, ...
                                    projection, displayfun)
% function [Acoarse m n] = resampling(A, x1, y1, nxy, samplestep, ...
%                                    projection, displayfun)
% Resampling the original in coarser resolution
% INPUTS:
%   (x1,y1) is the index of the upper/left corner
%   nxy is the linear size of the rectangular face (pixels)
%   samplestep is sampling step
%   projection is type of projection
% OUTPUT:
%   Acoarse is the resampling map where all "faces" are put row-wise
%   m is number of rectangular faces
%   n is the linear resolution of the faces
%   Acoarse is dimension [n] x [n*m] x [3]

switch projection
    case 'cube',
        % The topology of six faces is like this
        %        N
        %        ^
        %    W <-o-> E
        %        v
        %        S
        %      +---+
        %      | 6 |
        %  +---+---+---+---+
        %  | 4 | 1 | 2 | 3 |
        %  +---+---+---+---+
        %      | 5 |
        %      +---+
        %
        mx = 4;
        my = 3;
        
        subreg = [5 8 11 2 6 4];
    case 'mercator',
        % The topology of the face is like this (!)
        %        N
        %        ^
        %    W <-o-> E
        %        v
        %        S
        %      +---+
        %      | 1 |
        %      +---+
        %
        mx = 1;
        my = 1;
        subreg = [1]; %#ok
end

if isnan(nxy)
    nxy = [size(A,1) size(A,2)];
elseif isscalar(nxy)
    nxy = [nxy nxy];
end
% lower resolution
n = ceil(nxy./samplestep);
m = length(subreg);
j1 = (0:nxy(1)-1).';
j2 = (0:nxy(2)-1).';
if any(n.*samplestep > nxy) % this 'if' is not really needed
    % not exactly divided, pad with last index
    displayfun('Warning: resampling padding -> loss accuracy\n');
    j1(end+1:n(1)*samplestep) = nxy(1)-1;
    j2(end+1:n(2)*samplestep) = nxy(2)-1;
end
iy = bsxfun(@plus,j1,y1+((1:my)-1)*nxy(1)); % three faces on y direction
ix = bsxfun(@plus,j2,x1+((1:mx)-1)*nxy(2)); % four in x

% get submatrix
A = A(iy,ix,:);

% Get six-face only
nxy = n.*samplestep; % bug corrected
A = reshape(A, [nxy(1) my nxy(2) mx 3]);
A = reshape(permute(A, [1 3 2 4 5]), [nxy(1:2) mx*my 3]);
A = A(:,:,subreg,:); % six faces (nxy x nxy x 6 x 3)

szA = [n(1) n(2)*m];
% partitionning
A = reshape(A, [samplestep szA(1) samplestep szA(2) 3]);
% Put the subpixels in the last dimension
A = reshape(permute(A, [2 4 5 1 3]), szA(1), szA(2), 3, []);
% Cast and normalize to [0,1]
if strcmp(class(A),'uint8') % 0-255 RGB 
    A = double(A) / double(intmax(class(A)));
end
% Average the color on the new patch
szA = size(A);
Acoarse = zeros(szA(1:3),'double');
% for-loop is less memory demanding than one-shot mean
for k=1:size(A,3)
    Acoarse(:,:,k) = mean(A(:,:,k,:), 4);
end

end % resampling

%%
function IDX = GridIdx(Faces, FaceID, ID, n, projection)
% function IDX = GridIdx(Faces, FaceID, ID)
% Return grid index of one face (in a 2D array format)

switch projection
    case 'cube',
        % Filter
        ID = feval(class(FaceID),ID);
        F = Faces(FaceID==ID,:);
    case 'mercator',
        F = Faces; % Just one face!
end

F = reshape(F,[n(1) n(2) 4]);
IDX = [F(:,:,1)   F(:,end,2);
       F(end,:,4) F(end,end,3)];
   
end % GridIdx

Contact us at files@mathworks.com