function modelTour(this,time,long,lat,alt,heading,tilt,roll,varargin)
    %KML.MODELTOUR (long,lat,alt,heading,tilt,roll) Animates a 3D model (specified by the pair
    %  attribute 'model','modelfile.dae') in the position and orientation given by long,
    %  lat, alt, heading, tilt and roll. The time input controls how long the model takes
    %  to fly from one coordinate to the next.
    %
    %  Many model files can be found in the <a href="http://sketchup.google.com/3dwarehouse/">Google 3D Warehouse</a> website.
    %
    %   Copyright 2012 Rafael Fernandes de Oliveira (rafael@rafael.aero)
    %   $Revision: 1.4 $  $Date: 2012/02/08 16:00:00 $
    
    [long,lat,heading,tilt,roll] = this.checkUnit(long,lat,heading,tilt,roll);
    
    p = inputParser;
    
    nlat = numel(lat);
    
    p.addRequired('time',@(a)isnumeric(a) && isvector(a) &&~isempty(a) && numel(a)==nlat);
    p.addRequired('lat',@(a)isnumeric(a) && isvector(a) &&~isempty(a));
    p.addRequired('long',@(a)isnumeric(a) && isvector(a) &&~isempty(a) && numel(a)==nlat);
    p.addRequired('alt',@(a)isnumeric(a) && isvector(a) &&~isempty(a) && numel(a)==nlat);
    p.addRequired('heading',@(a)isnumeric(a) && isvector(a) &&~isempty(a) && numel(a)==nlat);
    p.addRequired('tilt',@(a)isnumeric(a) && isvector(a) &&~isempty(a) && numel(a)==nlat);
    p.addRequired('roll',@(a)isnumeric(a) && isvector(a) &&~isempty(a) && numel(a)==nlat);
    
    p.addParamValue('id','kml_modelTour',@ischar);
    p.addParamValue('name','kml_modelTour',@ischar);
    p.addParamValue('description','',@ischar);
    p.addParamValue('visibility',true,@islogical);
    p.addParamValue('model','',@ischar);
    p.addParamValue('scaleX',1,@(a)isnumeric(a) && (numel(a)==nlat || numel(a)==1));
    p.addParamValue('scaleY',1,@(a)isnumeric(a) && (numel(a)==nlat || numel(a)==1));
    p.addParamValue('scaleZ',1,@(a)isnumeric(a) && (numel(a)==nlat || numel(a)==1));
    p.addParamValue('scale',1, @(a)isnumeric(a) && (numel(a)==nlat || numel(a)==1));
    p.addParamValue('altitudeMode','relativeToGround',@(a)ismember(a,{'clampToGround','relativeToGround','absolute'}));
    
    p.addParamValue('tourName','Play Me!',@ischar);
    p.addParamValue('cameraMode','above',@(a)ismember(a,{'behind','above','fixed'}));
    p.addParamValue('cameraDistance',1e3,@isnumeric);
    
    
    p.parse(time,lat,long,alt,heading,tilt,roll,varargin{:});
    
    arg = p.Results;
    
    
    if isempty(arg.model)
        error('Missing model parameter')
    end
    
    arg.scaleX = arg.scaleX .*arg.scale;
    arg.scaleY = arg.scaleY .*arg.scale;
    arg.scaleZ = arg.scaleZ .*arg.scale;
    
    if numel(arg.scaleX)==1 && nlat>1
        arg.scaleX = repmat(arg.scaleX,size(lat));
    end
    
    if numel(arg.scaleY)==1 && nlat>1
        arg.scaleY = repmat(arg.scaleY,size(lat));
    end
    
    if numel(arg.scaleZ)==1 && nlat>1
        arg.scaleZ = repmat(arg.scaleZ,size(lat));
    end
    
    
    locationID    = [arg.id '_location'];
    orientationID = [arg.id '_orientation'];
    scaleID       = [arg.id '_scale'];
    
    placemark   = this.xml.createElement('Placemark');
    model       = this.xml.createElement('Model');
    location    = this.xml.createElement('Location');
    orientation = this.xml.createElement('Orientation');
    scale       = this.xml.createElement('Scale');
    link        = this.xml.createElement('Link');
    
    placemark.setAttribute('id',arg.id);
    placemark.appendChild(this.textNode('name',arg.name));
    placemark.appendChild(this.textNode('visibility',num2str(arg.visibility)));
    placemark.appendChild(this.textNode('description',arg.description));
    
    location.setAttribute('id',locationID);
    location.appendChild(this.textNode('latitude',  num2str(lat(1))));
    location.appendChild(this.textNode('longitude', num2str(long(1))));
    location.appendChild(this.textNode('altitude',  num2str(alt(1))));
    
    orientation.setAttribute('id',orientationID);
    orientation.appendChild(this.textNode('heading', num2str(heading(1))));
    orientation.appendChild(this.textNode('tilt',    num2str(tilt(1))));
    orientation.appendChild(this.textNode('roll',    num2str(roll(1))));
    
    scale.setAttribute('id',scaleID);
    scale.appendChild(this.textNode('x', num2str(arg.scaleX(1))));
    scale.appendChild(this.textNode('y', num2str(arg.scaleY(1))));
    scale.appendChild(this.textNode('z', num2str(arg.scaleZ(1))));
    
    link.appendChild(this.textNode('href',arg.model));
    
    model.setAttribute('id',['Model_' arg.id]);
    model.appendChild(this.textNode('altitudeMode',arg.altitudeMode));
    
    model.appendChild(location);
    model.appendChild(orientation);
    model.appendChild(scale);
    model.appendChild(link);
    
    placemark.appendChild(model);
    this.doc.appendChild(placemark);
    
    tour     = this.xml.createElement('gx:Tour');
    playlist = this.xml.createElement('gx:Playlist');
    tour.appendChild(this.textNode('name',arg.tourName));
    
    for i = 2:nlat-1
        
        dT = time(i)-time(i-1);
        
        % Update object position
        animUpdate  = this.xml.createElement('gx:AnimatedUpdate');
        update      = this.xml.createElement('Update');
        change      = this.xml.createElement('Change');
        location    = this.xml.createElement('Location');
        orientation = this.xml.createElement('Orientation');
        scale       = this.xml.createElement('Scale');
        
        animUpdate.appendChild(this.textNode('gx:duration',num2str(dT)));
        
        location.setAttribute('targetId',locationID);
        location.appendChild(this.textNode('latitude',  num2str(lat(i))));
        location.appendChild(this.textNode('longitude', num2str(long(i))));
        location.appendChild(this.textNode('altitude',  num2str(alt(i))));
        
        orientation.setAttribute('targetId',orientationID);
        orientation.appendChild(this.textNode('heading', num2str(heading(i))));
        orientation.appendChild(this.textNode('tilt',    num2str(tilt(i))));
        orientation.appendChild(this.textNode('roll',    num2str(roll(i))));
        
        scale.setAttribute('targetId',scaleID);
        scale.appendChild(this.textNode('x', num2str(arg.scaleX(i))));
        scale.appendChild(this.textNode('y', num2str(arg.scaleY(i))));
        scale.appendChild(this.textNode('z', num2str(arg.scaleZ(i))));
        
        change.appendChild(location);
        change.appendChild(orientation);
        change.appendChild(scale);
        
        update.appendChild(this.textNode('targetHref',''));
        update.appendChild(change);
        animUpdate.appendChild(update);
        
        playlist.appendChild(animUpdate);
        
        % Update camera position
        switch arg.cameraMode
            case 'fixed'
                wait  = this.xml.createElement('gx:Wait');
                wait.appendChild(this.textNode('gx:duration',num2str(dT)));
                playlist.appendChild(wait);
            case 'behind'
                dS = [long(i+1) - long(i-1); lat(i+1) - lat(i-1);];
                dS = arg.cameraDistance * dS./sqrt(sum(dS.^2))/1e6;
                
                flyTo = this.xml.createElement('gx:FlyTo');
                flyTo.appendChild(this.textNode('gx:duration',num2str(dT)));
                flyTo.appendChild(this.textNode('gx:flyToMode','smooth'));
                camera = this.xml.createElement('Camera');
                camera.appendChild(this.textNode('longitude',num2str(long(i)-dS(1))));
                camera.appendChild(this.textNode('latitude',num2str(lat(i)-dS(2))));
                camera.appendChild(this.textNode('altitude',num2str(alt(i))));
                h = heading(i)-180;
                
                if h < -360
                    h = 720+h;
                elseif h>360
                    h = h-720;
                end
                    
                camera.appendChild(this.textNode('heading',num2str(h)));
                camera.appendChild(this.textNode('tilt',num2str(90)));
                camera.appendChild(this.textNode('roll',num2str(0)));
                camera.appendChild(this.textNode('altitudeMode',arg.altitudeMode));
                
                flyTo.appendChild(camera);
                playlist.appendChild(flyTo);
            case 'above'
                
                flyTo = this.xml.createElement('gx:FlyTo');
                flyTo.appendChild(this.textNode('gx:duration',num2str(dT)));
                flyTo.appendChild(this.textNode('gx:flyToMode','smooth'));
                lookAt = this.xml.createElement('LookAt');
                lookAt.appendChild(this.textNode('longitude',num2str(long(i))));
                lookAt.appendChild(this.textNode('latitude',num2str(lat(i))));
                lookAt.appendChild(this.textNode('altitude',num2str(alt(i) + arg.cameraDistance)));
                lookAt.appendChild(this.textNode('altitudeMode',arg.altitudeMode));
                
                flyTo.appendChild(lookAt);
                playlist.appendChild(flyTo);
        end
    end
    
    tour.appendChild(playlist);
    this.doc.appendChild(tour);
    
    
end