classdef kml < handle %KML(name) Create a KML toolbox object, that allows you to plot from MATLAB to Google Earth % This class-based toolbox allows you to create many different plots in Google Earth, by automatically creating the required xml-based kml files without user interaction. % % With it, you can create: % - line plots, scatter plots % - 2D and 3D contours % - 2D and 3D polygons % - quiver plots % - write text in a given point % - place 3D models % - overlay images % - transfer more complex figures as images % - folders, subfolders,... to aggregate similar plots % % For some usage examples, check the code at .\test\RunTests.m % % If you enjoy it, just drop me an email at kml@rafael.aero saying for what you're using it :). % % Copyright 2012 Rafael Fernandes de Oliveira (rafael@rafael.aero) % $Revision: 2.3 $ $Date: 2012/09/05 08:00:00 $ properties %(Access = private, Transient) xml doc parent = [] filename = '' name = '' unit = 'deg' zip = true includeFiles; end properties (Access = private) target = 'earth' end methods function this = kml(name,parent) if nargin <1 name = 'Unnamed KML'; end this.name = name; if nargin <2 parent = []; else if ~isa(parent,'kml') error('Invalid parent, pass only a kml object or a kml folder') end end this.parent = parent; if isempty(parent) this.xml = com.mathworks.xml.XMLUtils.createDocument('kml'); kmlNode = this.xml.getDocumentElement; kmlNode.setAttribute('xmlns','http://www.opengis.net/kml/2.2'); kmlNode.setAttribute('xmlns:gx','http://www.google.com/kml/ext/2.2'); kmlNode.setAttribute('xmlns:kml','http://www.opengis.net/kml/2.2'); kmlNode.setAttribute('xmlns:atom','http://www.w3.org/2005/Atom'); this.doc = this.xml.createElement('Document'); this.doc.appendChild(this.textNode('name',name)); kmlNode.appendChild(this.doc); else this.doc = parent.xml.createElement('Folder'); this.xml = parent.xml; this.doc.appendChild(this.textNode('name',name)); this.doc.appendChild(this.textNode('id', name)); parent.doc.appendChild(this.doc); end end function clear(this) if isempty(this.parent) %not a folder, just create a new kml class and reuse it's xml nodes a = kml(this.name); this.doc = a.doc; this.xml = a.xml; this.includeFiles = ''; a.delete; else this.parent.doc.removeChild(this.doc); a = kml(this.name,this.parent); this.doc = a.doc; this.xml = a.xml; a.delete; end end function addIncludeFile(this,varargin) K = this; while ~isempty(K.parent) K = K.parent; end newFiles = varargin; for i = 1:numel(newFiles) K.includeFiles{end+1} = newFiles{i}; end end function save(this,filename, compact) if ~isempty(this.parent) warning('KML:incompletefile','You are saving only a folder of the KML file, not the full KML, this will not load on any viewer'); end if nargin < 2 if isempty(this.filename) if strcmpi(this.name,'Unnamed KML') [tmpvar,filename] = fileparts(tempname(cd)); filename = [filename '.kml']; warning('KML:tempfile','Please delete file %s later, if not required...',filename); else filename = this.name; end else filename = this.filename; end end if nargin < 3 compact = this.zip; else this.zip = compact; end [p,fn,ext] = fileparts(filename); filename = fullfile(p,[fn '.kml']); xmlwrite(filename,this.xml); if this.zip [p,fn,ext] = fileparts(filename); kmzFilename = fullfile(p,[fn '.kmz']); zip(kmzFilename,vertcat({filename},this.includeFiles(:)).'); delete(filename); movefile([kmzFilename '.zip'],kmzFilename,'f'); this.filename = kmzFilename; else this.filename = filename; end end function viewKML(this) tmpfile = [tempname '.kml']; xmlwrite(tmpfile ,this.xml); edit(tmpfile); delete(tmpfile); end function run(this) this.save; switch true case ispc system(['start "kmltoolbox" "' this.filename '"']); case ismac system(['open' ' "' this.filename '"']); otherwise disp('The KML file has been saved, open it in Google Earth'); end end function useRadians(this) this.unit = 'rad'; end function useDegrees(this) this.unit = 'deg'; end function setTarget(this,target) target = lower(target); assert(ismember(target,{'earth','moon','sky','mars'}),'Target must be one of the following: ''earth'' ''moon'' ''mars'' or ''sky''') this.target = target; kmlNode = this.xml.getDocumentElement; if ~strcmpi(this.target,'earth') kmlNode.setAttribute('hint',['target=' this.target]); end end function varargout = checkUnit(this,varargin) varargout = varargin; parent = this; while ~isempty(parent.parent) parent = parent.parent; end if strcmpi(parent.unit,'deg') %do nothing elseif strcmpi(parent.unit,'rad') for i = 1:numel(varargin) varargout{i} = varargin{i}.* (180/pi); end else error('invalid angular unit %s', parent.unit); end end function disp(this) display(this); end function display(this) if getpref('kmltoolbox','ShowDisclaimer',true) disp(sprintf('\n')); disp(' _ ____ __ _ _____ _ _ ') disp(' | |/ / \/ | | |_ _|__ ___| | |__ _____ __') disp(' | '' <| |\/| | |__ | |/ _ \/ _ \ | ''_ \/ _ \ \ /') disp(' |_|\_\_| |_|____| |_|\___/\___/_|_.__/\___/_\_\') disp(sprintf('\n')); disp('Copyright 2012 Rafael Fernandes de Oliveira (rafael@rafael.aero)') disp(sprintf('\n')); disp('If you enjoy it, just drop me an email at kml@rafael.aero saying for what you''re using it :-)') disp(sprintf('\n')); disp('To install the toolbox, and suppress this message, run kml.install') end end end methods %(Access = private) function node = textNode(this,Name,Text) node = this.xml.createElement(Name); node.appendChild(this.xml.createTextNode(Text)); end end methods (Static) iconURL = parseIconURL(iconURL) im = captureScreen(view) function colorHex = color2kmlHex(color) if numel(color) ==3 color(4) = 1; end color = min(max(floor(color*255),0),255); [r,g,b,a] = deal(color(1),color(2),color(3),color(4)); [rhex, ghex, bhex, ahex ]= deal(dec2hex(r),dec2hex(g),dec2hex(b),dec2hex(a)); if length(rhex)==1,rhex=['0' rhex];end if length(ghex)==1,ghex=['0' ghex];end if length(bhex)==1,bhex=['0' bhex];end if length(ahex)==1,ahex=['0' ahex];end colorHex = [ahex bhex ghex rhex]; end function ID = getTempID(rootID) [tmp,tmpID] = fileparts(tempname); ID = [rootID tmpID]; end function dist = ll2dist(lon1, lat1,lon2, lat2) %Input in radians - Calculate distance using Haversine formula R = 6378137; dLat = (lat2-lat1); dLon = (lon2-lon1); a = sqr(sin(dLat/2)) + cos(lat1) .* cos(lat2) .* sqr(sin(dLon/2)); c = 2 * atan(sqrt(a)./sqrt(1-a)); dist = R * c; end function install() clc nl = sprintf('\n'); disp(nl); disp(' _ ____ __ _ _____ _ _ ') disp(' | |/ / \/ | | |_ _|__ ___| | |__ _____ __') disp(' | '' <| |\/| | |__ | |/ _ \/ _ \ | ''_ \/ _ \ \ /') disp(' |_|\_\_| |_|____| |_|\___/\___/_|_.__/\___/_\_\') disp(nl); disp('This will add the KML toolbox to your MATLAB path, at the current location'); disp('If you prefer to have it located somewhere else, first copy the whole folder'); disp('to the place you want, and then run kml.install from there!'); disp(nl); p = fileparts(fileparts(mfilename('fullpath'))); disp(sprintf('Toolbox Path: %s',p)); disp(nl); s = input('Continue [Y]/N? ','s'); if strcmpi(s,'n') disp('Instalation aborted!') return end disp(nl); disp('Adding toolbox folder to the MATLAB path...') disp(nl); disp('(In some systems, this might require administrative rights, sorry for that)') disp(nl); addpath(p); addpath(fullfile(p,'html')); r = savepath; if r == 1 disp('Error saving your path. Please add the toolbox folder manually to your MATLAB path') end disp('Installing the toolbox help...') warning('off','MATLAB:doc:DocNotInstalled') builddocsearchdb(fullfile(p,'html')) warning('on','MATLAB:doc:DocNotInstalled') setpref('kmltoolbox','ShowDisclaimer',false); clc disp(nl); disp(' _ ____ __ _ _____ _ _ ') disp(' | |/ / \/ | | |_ _|__ ___| | |__ _____ __') disp(' | '' <| |\/| | |__ | |/ _ \/ _ \ | ''_ \/ _ \ \ /') disp(' |_|\_\_| |_|____| |_|\___/\___/_|_.__/\___/_\_\') disp(nl); disp(nl); disp(' Toolbox installed successfully!') disp(nl); disp('To take a look in the toolbox contents, run: >> kmldoc'); disp('To get help for the toolbox functions, run: >> kmldoc kml.FunctionName'); disp(nl); end end end