diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..92974a5 --- /dev/null +++ b/404.html @@ -0,0 +1,972 @@ + + + + + + + + + + + + + + + + + + + + + + QGIS Animation Workbench + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 0000000..1cf13b9 Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/javascripts/bundle.51d95adb.min.js b/assets/javascripts/bundle.51d95adb.min.js new file mode 100644 index 0000000..b20ec68 --- /dev/null +++ b/assets/javascripts/bundle.51d95adb.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var Hi=Object.create;var xr=Object.defineProperty;var Pi=Object.getOwnPropertyDescriptor;var $i=Object.getOwnPropertyNames,kt=Object.getOwnPropertySymbols,Ii=Object.getPrototypeOf,Er=Object.prototype.hasOwnProperty,an=Object.prototype.propertyIsEnumerable;var on=(e,t,r)=>t in e?xr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))Er.call(t,r)&&on(e,r,t[r]);if(kt)for(var r of kt(t))an.call(t,r)&&on(e,r,t[r]);return e};var sn=(e,t)=>{var r={};for(var n in e)Er.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(e!=null&&kt)for(var n of kt(e))t.indexOf(n)<0&&an.call(e,n)&&(r[n]=e[n]);return r};var Ht=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Fi=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of $i(t))!Er.call(e,o)&&o!==r&&xr(e,o,{get:()=>t[o],enumerable:!(n=Pi(t,o))||n.enumerable});return e};var yt=(e,t,r)=>(r=e!=null?Hi(Ii(e)):{},Fi(t||!e||!e.__esModule?xr(r,"default",{value:e,enumerable:!0}):r,e));var fn=Ht((wr,cn)=>{(function(e,t){typeof wr=="object"&&typeof cn!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(wr,function(){"use strict";function e(r){var n=!0,o=!1,i=null,a={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function s(T){return!!(T&&T!==document&&T.nodeName!=="HTML"&&T.nodeName!=="BODY"&&"classList"in T&&"contains"in T.classList)}function f(T){var Ke=T.type,We=T.tagName;return!!(We==="INPUT"&&a[Ke]&&!T.readOnly||We==="TEXTAREA"&&!T.readOnly||T.isContentEditable)}function c(T){T.classList.contains("focus-visible")||(T.classList.add("focus-visible"),T.setAttribute("data-focus-visible-added",""))}function u(T){T.hasAttribute("data-focus-visible-added")&&(T.classList.remove("focus-visible"),T.removeAttribute("data-focus-visible-added"))}function p(T){T.metaKey||T.altKey||T.ctrlKey||(s(r.activeElement)&&c(r.activeElement),n=!0)}function m(T){n=!1}function d(T){s(T.target)&&(n||f(T.target))&&c(T.target)}function h(T){s(T.target)&&(T.target.classList.contains("focus-visible")||T.target.hasAttribute("data-focus-visible-added"))&&(o=!0,window.clearTimeout(i),i=window.setTimeout(function(){o=!1},100),u(T.target))}function v(T){document.visibilityState==="hidden"&&(o&&(n=!0),B())}function B(){document.addEventListener("mousemove",z),document.addEventListener("mousedown",z),document.addEventListener("mouseup",z),document.addEventListener("pointermove",z),document.addEventListener("pointerdown",z),document.addEventListener("pointerup",z),document.addEventListener("touchmove",z),document.addEventListener("touchstart",z),document.addEventListener("touchend",z)}function re(){document.removeEventListener("mousemove",z),document.removeEventListener("mousedown",z),document.removeEventListener("mouseup",z),document.removeEventListener("pointermove",z),document.removeEventListener("pointerdown",z),document.removeEventListener("pointerup",z),document.removeEventListener("touchmove",z),document.removeEventListener("touchstart",z),document.removeEventListener("touchend",z)}function z(T){T.target.nodeName&&T.target.nodeName.toLowerCase()==="html"||(n=!1,re())}document.addEventListener("keydown",p,!0),document.addEventListener("mousedown",m,!0),document.addEventListener("pointerdown",m,!0),document.addEventListener("touchstart",m,!0),document.addEventListener("visibilitychange",v,!0),B(),r.addEventListener("focus",d,!0),r.addEventListener("blur",h,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var un=Ht(Sr=>{(function(e){var t=function(){try{return!!Symbol.iterator}catch(c){return!1}},r=t(),n=function(c){var u={next:function(){var p=c.shift();return{done:p===void 0,value:p}}};return r&&(u[Symbol.iterator]=function(){return u}),u},o=function(c){return encodeURIComponent(c).replace(/%20/g,"+")},i=function(c){return decodeURIComponent(String(c).replace(/\+/g," "))},a=function(){var c=function(p){Object.defineProperty(this,"_entries",{writable:!0,value:{}});var m=typeof p;if(m!=="undefined")if(m==="string")p!==""&&this._fromString(p);else if(p instanceof c){var d=this;p.forEach(function(re,z){d.append(z,re)})}else if(p!==null&&m==="object")if(Object.prototype.toString.call(p)==="[object Array]")for(var h=0;hd[0]?1:0}),c._entries&&(c._entries={});for(var p=0;p1?i(d[1]):"")}})})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Sr);(function(e){var t=function(){try{var o=new e.URL("b","http://a");return o.pathname="c d",o.href==="http://a/c%20d"&&o.searchParams}catch(i){return!1}},r=function(){var o=e.URL,i=function(f,c){typeof f!="string"&&(f=String(f)),c&&typeof c!="string"&&(c=String(c));var u=document,p;if(c&&(e.location===void 0||c!==e.location.href)){c=c.toLowerCase(),u=document.implementation.createHTMLDocument(""),p=u.createElement("base"),p.href=c,u.head.appendChild(p);try{if(p.href.indexOf(c)!==0)throw new Error(p.href)}catch(T){throw new Error("URL unable to set base "+c+" due to "+T)}}var m=u.createElement("a");m.href=f,p&&(u.body.appendChild(m),m.href=m.href);var d=u.createElement("input");if(d.type="url",d.value=f,m.protocol===":"||!/:/.test(m.href)||!d.checkValidity()&&!c)throw new TypeError("Invalid URL");Object.defineProperty(this,"_anchorElement",{value:m});var h=new e.URLSearchParams(this.search),v=!0,B=!0,re=this;["append","delete","set"].forEach(function(T){var Ke=h[T];h[T]=function(){Ke.apply(h,arguments),v&&(B=!1,re.search=h.toString(),B=!0)}}),Object.defineProperty(this,"searchParams",{value:h,enumerable:!0});var z=void 0;Object.defineProperty(this,"_updateSearchParams",{enumerable:!1,configurable:!1,writable:!1,value:function(){this.search!==z&&(z=this.search,B&&(v=!1,this.searchParams._fromString(this.search),v=!0))}})},a=i.prototype,s=function(f){Object.defineProperty(a,f,{get:function(){return this._anchorElement[f]},set:function(c){this._anchorElement[f]=c},enumerable:!0})};["hash","host","hostname","port","protocol"].forEach(function(f){s(f)}),Object.defineProperty(a,"search",{get:function(){return this._anchorElement.search},set:function(f){this._anchorElement.search=f,this._updateSearchParams()},enumerable:!0}),Object.defineProperties(a,{toString:{get:function(){var f=this;return function(){return f.href}}},href:{get:function(){return this._anchorElement.href.replace(/\?$/,"")},set:function(f){this._anchorElement.href=f,this._updateSearchParams()},enumerable:!0},pathname:{get:function(){return this._anchorElement.pathname.replace(/(^\/?)/,"/")},set:function(f){this._anchorElement.pathname=f},enumerable:!0},origin:{get:function(){var f={"http:":80,"https:":443,"ftp:":21}[this._anchorElement.protocol],c=this._anchorElement.port!=f&&this._anchorElement.port!=="";return this._anchorElement.protocol+"//"+this._anchorElement.hostname+(c?":"+this._anchorElement.port:"")},enumerable:!0},password:{get:function(){return""},set:function(f){},enumerable:!0},username:{get:function(){return""},set:function(f){},enumerable:!0}}),i.createObjectURL=function(f){return o.createObjectURL.apply(o,arguments)},i.revokeObjectURL=function(f){return o.revokeObjectURL.apply(o,arguments)},e.URL=i};if(t()||r(),e.location!==void 0&&!("origin"in e.location)){var n=function(){return e.location.protocol+"//"+e.location.hostname+(e.location.port?":"+e.location.port:"")};try{Object.defineProperty(e.location,"origin",{get:n,enumerable:!0})}catch(o){setInterval(function(){e.location.origin=n()},100)}}})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Sr)});var Qr=Ht((Lt,Kr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Lt=="object"&&typeof Kr=="object"?Kr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Lt=="object"?Lt.ClipboardJS=r():t.ClipboardJS=r()})(Lt,function(){return function(){var e={686:function(n,o,i){"use strict";i.d(o,{default:function(){return ki}});var a=i(279),s=i.n(a),f=i(370),c=i.n(f),u=i(817),p=i.n(u);function m(j){try{return document.execCommand(j)}catch(O){return!1}}var d=function(O){var w=p()(O);return m("cut"),w},h=d;function v(j){var O=document.documentElement.getAttribute("dir")==="rtl",w=document.createElement("textarea");w.style.fontSize="12pt",w.style.border="0",w.style.padding="0",w.style.margin="0",w.style.position="absolute",w.style[O?"right":"left"]="-9999px";var k=window.pageYOffset||document.documentElement.scrollTop;return w.style.top="".concat(k,"px"),w.setAttribute("readonly",""),w.value=j,w}var B=function(O,w){var k=v(O);w.container.appendChild(k);var F=p()(k);return m("copy"),k.remove(),F},re=function(O){var w=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},k="";return typeof O=="string"?k=B(O,w):O instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(O==null?void 0:O.type)?k=B(O.value,w):(k=p()(O),m("copy")),k},z=re;function T(j){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?T=function(w){return typeof w}:T=function(w){return w&&typeof Symbol=="function"&&w.constructor===Symbol&&w!==Symbol.prototype?"symbol":typeof w},T(j)}var Ke=function(){var O=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},w=O.action,k=w===void 0?"copy":w,F=O.container,q=O.target,Le=O.text;if(k!=="copy"&&k!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(q!==void 0)if(q&&T(q)==="object"&&q.nodeType===1){if(k==="copy"&&q.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(k==="cut"&&(q.hasAttribute("readonly")||q.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Le)return z(Le,{container:F});if(q)return k==="cut"?h(q):z(q,{container:F})},We=Ke;function Ie(j){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Ie=function(w){return typeof w}:Ie=function(w){return w&&typeof Symbol=="function"&&w.constructor===Symbol&&w!==Symbol.prototype?"symbol":typeof w},Ie(j)}function Ti(j,O){if(!(j instanceof O))throw new TypeError("Cannot call a class as a function")}function nn(j,O){for(var w=0;w0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof F.action=="function"?F.action:this.defaultAction,this.target=typeof F.target=="function"?F.target:this.defaultTarget,this.text=typeof F.text=="function"?F.text:this.defaultText,this.container=Ie(F.container)==="object"?F.container:document.body}},{key:"listenClick",value:function(F){var q=this;this.listener=c()(F,"click",function(Le){return q.onClick(Le)})}},{key:"onClick",value:function(F){var q=F.delegateTarget||F.currentTarget,Le=this.action(q)||"copy",Rt=We({action:Le,container:this.container,target:this.target(q),text:this.text(q)});this.emit(Rt?"success":"error",{action:Le,text:Rt,trigger:q,clearSelection:function(){q&&q.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(F){return yr("action",F)}},{key:"defaultTarget",value:function(F){var q=yr("target",F);if(q)return document.querySelector(q)}},{key:"defaultText",value:function(F){return yr("text",F)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(F){var q=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return z(F,q)}},{key:"cut",value:function(F){return h(F)}},{key:"isSupported",value:function(){var F=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],q=typeof F=="string"?[F]:F,Le=!!document.queryCommandSupported;return q.forEach(function(Rt){Le=Le&&!!document.queryCommandSupported(Rt)}),Le}}]),w}(s()),ki=Ri},828:function(n){var o=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function a(s,f){for(;s&&s.nodeType!==o;){if(typeof s.matches=="function"&&s.matches(f))return s;s=s.parentNode}}n.exports=a},438:function(n,o,i){var a=i(828);function s(u,p,m,d,h){var v=c.apply(this,arguments);return u.addEventListener(m,v,h),{destroy:function(){u.removeEventListener(m,v,h)}}}function f(u,p,m,d,h){return typeof u.addEventListener=="function"?s.apply(null,arguments):typeof m=="function"?s.bind(null,document).apply(null,arguments):(typeof u=="string"&&(u=document.querySelectorAll(u)),Array.prototype.map.call(u,function(v){return s(v,p,m,d,h)}))}function c(u,p,m,d){return function(h){h.delegateTarget=a(h.target,p),h.delegateTarget&&d.call(u,h)}}n.exports=f},879:function(n,o){o.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},o.nodeList=function(i){var a=Object.prototype.toString.call(i);return i!==void 0&&(a==="[object NodeList]"||a==="[object HTMLCollection]")&&"length"in i&&(i.length===0||o.node(i[0]))},o.string=function(i){return typeof i=="string"||i instanceof String},o.fn=function(i){var a=Object.prototype.toString.call(i);return a==="[object Function]"}},370:function(n,o,i){var a=i(879),s=i(438);function f(m,d,h){if(!m&&!d&&!h)throw new Error("Missing required arguments");if(!a.string(d))throw new TypeError("Second argument must be a String");if(!a.fn(h))throw new TypeError("Third argument must be a Function");if(a.node(m))return c(m,d,h);if(a.nodeList(m))return u(m,d,h);if(a.string(m))return p(m,d,h);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(m,d,h){return m.addEventListener(d,h),{destroy:function(){m.removeEventListener(d,h)}}}function u(m,d,h){return Array.prototype.forEach.call(m,function(v){v.addEventListener(d,h)}),{destroy:function(){Array.prototype.forEach.call(m,function(v){v.removeEventListener(d,h)})}}}function p(m,d,h){return s(document.body,m,d,h)}n.exports=f},817:function(n){function o(i){var a;if(i.nodeName==="SELECT")i.focus(),a=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var s=i.hasAttribute("readonly");s||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),s||i.removeAttribute("readonly"),a=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var f=window.getSelection(),c=document.createRange();c.selectNodeContents(i),f.removeAllRanges(),f.addRange(c),a=f.toString()}return a}n.exports=o},279:function(n){function o(){}o.prototype={on:function(i,a,s){var f=this.e||(this.e={});return(f[i]||(f[i]=[])).push({fn:a,ctx:s}),this},once:function(i,a,s){var f=this;function c(){f.off(i,c),a.apply(s,arguments)}return c._=a,this.on(i,c,s)},emit:function(i){var a=[].slice.call(arguments,1),s=((this.e||(this.e={}))[i]||[]).slice(),f=0,c=s.length;for(f;f{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var is=/["'&<>]/;Jo.exports=as;function as(e){var t=""+e,r=is.exec(t);if(!r)return t;var n,o="",i=0,a=0;for(i=r.index;i0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function W(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),o,i=[],a;try{for(;(t===void 0||t-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(s){a={error:s}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(a)throw a.error}}return i}function D(e,t,r){if(r||arguments.length===2)for(var n=0,o=t.length,i;n1||s(m,d)})})}function s(m,d){try{f(n[m](d))}catch(h){p(i[0][3],h)}}function f(m){m.value instanceof Xe?Promise.resolve(m.value.v).then(c,u):p(i[0][2],m)}function c(m){s("next",m)}function u(m){s("throw",m)}function p(m,d){m(d),i.shift(),i.length&&s(i[0][0],i[0][1])}}function mn(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof xe=="function"?xe(e):e[Symbol.iterator](),r={},n("next"),n("throw"),n("return"),r[Symbol.asyncIterator]=function(){return this},r);function n(i){r[i]=e[i]&&function(a){return new Promise(function(s,f){a=e[i](a),o(s,f,a.done,a.value)})}}function o(i,a,s,f){Promise.resolve(f).then(function(c){i({value:c,done:s})},a)}}function A(e){return typeof e=="function"}function at(e){var t=function(n){Error.call(n),n.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var $t=at(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(n,o){return o+1+") "+n.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function De(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Fe=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,n,o,i;if(!this.closed){this.closed=!0;var a=this._parentage;if(a)if(this._parentage=null,Array.isArray(a))try{for(var s=xe(a),f=s.next();!f.done;f=s.next()){var c=f.value;c.remove(this)}}catch(v){t={error:v}}finally{try{f&&!f.done&&(r=s.return)&&r.call(s)}finally{if(t)throw t.error}}else a.remove(this);var u=this.initialTeardown;if(A(u))try{u()}catch(v){i=v instanceof $t?v.errors:[v]}var p=this._finalizers;if(p){this._finalizers=null;try{for(var m=xe(p),d=m.next();!d.done;d=m.next()){var h=d.value;try{dn(h)}catch(v){i=i!=null?i:[],v instanceof $t?i=D(D([],W(i)),W(v.errors)):i.push(v)}}}catch(v){n={error:v}}finally{try{d&&!d.done&&(o=m.return)&&o.call(m)}finally{if(n)throw n.error}}}if(i)throw new $t(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)dn(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&De(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&De(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Or=Fe.EMPTY;function It(e){return e instanceof Fe||e&&"closed"in e&&A(e.remove)&&A(e.add)&&A(e.unsubscribe)}function dn(e){A(e)?e():e.unsubscribe()}var Ae={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var st={setTimeout:function(e,t){for(var r=[],n=2;n0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var n=this,o=this,i=o.hasError,a=o.isStopped,s=o.observers;return i||a?Or:(this.currentObservers=null,s.push(r),new Fe(function(){n.currentObservers=null,De(s,r)}))},t.prototype._checkFinalizedStatuses=function(r){var n=this,o=n.hasError,i=n.thrownError,a=n.isStopped;o?r.error(i):a&&r.complete()},t.prototype.asObservable=function(){var r=new U;return r.source=this,r},t.create=function(r,n){return new wn(r,n)},t}(U);var wn=function(e){ne(t,e);function t(r,n){var o=e.call(this)||this;return o.destination=r,o.source=n,o}return t.prototype.next=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.next)===null||o===void 0||o.call(n,r)},t.prototype.error=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.error)===null||o===void 0||o.call(n,r)},t.prototype.complete=function(){var r,n;(n=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||n===void 0||n.call(r)},t.prototype._subscribe=function(r){var n,o;return(o=(n=this.source)===null||n===void 0?void 0:n.subscribe(r))!==null&&o!==void 0?o:Or},t}(E);var Et={now:function(){return(Et.delegate||Date).now()},delegate:void 0};var wt=function(e){ne(t,e);function t(r,n,o){r===void 0&&(r=1/0),n===void 0&&(n=1/0),o===void 0&&(o=Et);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=n,i._timestampProvider=o,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=n===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,n),i}return t.prototype.next=function(r){var n=this,o=n.isStopped,i=n._buffer,a=n._infiniteTimeWindow,s=n._timestampProvider,f=n._windowTime;o||(i.push(r),!a&&i.push(s.now()+f)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var n=this._innerSubscribe(r),o=this,i=o._infiniteTimeWindow,a=o._buffer,s=a.slice(),f=0;f0?e.prototype.requestAsyncId.call(this,r,n,o):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,n,o){var i;if(o===void 0&&(o=0),o!=null?o>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,n,o);var a=r.actions;n!=null&&((i=a[a.length-1])===null||i===void 0?void 0:i.id)!==n&&(ut.cancelAnimationFrame(n),r._scheduled=void 0)},t}(Ut);var On=function(e){ne(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var n=this._scheduled;this._scheduled=void 0;var o=this.actions,i;r=r||o.shift();do if(i=r.execute(r.state,r.delay))break;while((r=o[0])&&r.id===n&&o.shift());if(this._active=!1,i){for(;(r=o[0])&&r.id===n&&o.shift();)r.unsubscribe();throw i}},t}(Wt);var we=new On(Tn);var R=new U(function(e){return e.complete()});function Dt(e){return e&&A(e.schedule)}function kr(e){return e[e.length-1]}function Qe(e){return A(kr(e))?e.pop():void 0}function Se(e){return Dt(kr(e))?e.pop():void 0}function Vt(e,t){return typeof kr(e)=="number"?e.pop():t}var pt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function zt(e){return A(e==null?void 0:e.then)}function Nt(e){return A(e[ft])}function qt(e){return Symbol.asyncIterator&&A(e==null?void 0:e[Symbol.asyncIterator])}function Kt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Ki(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Qt=Ki();function Yt(e){return A(e==null?void 0:e[Qt])}function Gt(e){return ln(this,arguments,function(){var r,n,o,i;return Pt(this,function(a){switch(a.label){case 0:r=e.getReader(),a.label=1;case 1:a.trys.push([1,,9,10]),a.label=2;case 2:return[4,Xe(r.read())];case 3:return n=a.sent(),o=n.value,i=n.done,i?[4,Xe(void 0)]:[3,5];case 4:return[2,a.sent()];case 5:return[4,Xe(o)];case 6:return[4,a.sent()];case 7:return a.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function Bt(e){return A(e==null?void 0:e.getReader)}function $(e){if(e instanceof U)return e;if(e!=null){if(Nt(e))return Qi(e);if(pt(e))return Yi(e);if(zt(e))return Gi(e);if(qt(e))return _n(e);if(Yt(e))return Bi(e);if(Bt(e))return Ji(e)}throw Kt(e)}function Qi(e){return new U(function(t){var r=e[ft]();if(A(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function Yi(e){return new U(function(t){for(var r=0;r=2;return function(n){return n.pipe(e?_(function(o,i){return e(o,i,n)}):me,Oe(1),r?He(t):zn(function(){return new Xt}))}}function Nn(){for(var e=[],t=0;t=2,!0))}function fe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new E}:t,n=e.resetOnError,o=n===void 0?!0:n,i=e.resetOnComplete,a=i===void 0?!0:i,s=e.resetOnRefCountZero,f=s===void 0?!0:s;return function(c){var u,p,m,d=0,h=!1,v=!1,B=function(){p==null||p.unsubscribe(),p=void 0},re=function(){B(),u=m=void 0,h=v=!1},z=function(){var T=u;re(),T==null||T.unsubscribe()};return g(function(T,Ke){d++,!v&&!h&&B();var We=m=m!=null?m:r();Ke.add(function(){d--,d===0&&!v&&!h&&(p=jr(z,f))}),We.subscribe(Ke),!u&&d>0&&(u=new et({next:function(Ie){return We.next(Ie)},error:function(Ie){v=!0,B(),p=jr(re,o,Ie),We.error(Ie)},complete:function(){h=!0,B(),p=jr(re,a),We.complete()}}),$(T).subscribe(u))})(c)}}function jr(e,t){for(var r=[],n=2;ne.next(document)),e}function K(e,t=document){return Array.from(t.querySelectorAll(e))}function V(e,t=document){let r=se(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function se(e,t=document){return t.querySelector(e)||void 0}function _e(){return document.activeElement instanceof HTMLElement&&document.activeElement||void 0}function tr(e){return L(b(document.body,"focusin"),b(document.body,"focusout")).pipe(ke(1),l(()=>{let t=_e();return typeof t!="undefined"?e.contains(t):!1}),N(e===_e()),Y())}function Be(e){return{x:e.offsetLeft,y:e.offsetTop}}function Yn(e){return L(b(window,"load"),b(window,"resize")).pipe(Ce(0,we),l(()=>Be(e)),N(Be(e)))}function rr(e){return{x:e.scrollLeft,y:e.scrollTop}}function dt(e){return L(b(e,"scroll"),b(window,"resize")).pipe(Ce(0,we),l(()=>rr(e)),N(rr(e)))}var Bn=function(){if(typeof Map!="undefined")return Map;function e(t,r){var n=-1;return t.some(function(o,i){return o[0]===r?(n=i,!0):!1}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(r){var n=e(this.__entries__,r),o=this.__entries__[n];return o&&o[1]},t.prototype.set=function(r,n){var o=e(this.__entries__,r);~o?this.__entries__[o][1]=n:this.__entries__.push([r,n])},t.prototype.delete=function(r){var n=this.__entries__,o=e(n,r);~o&&n.splice(o,1)},t.prototype.has=function(r){return!!~e(this.__entries__,r)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(r,n){n===void 0&&(n=null);for(var o=0,i=this.__entries__;o0},e.prototype.connect_=function(){!zr||this.connected_||(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),xa?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){!zr||!this.connected_||(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(t){var r=t.propertyName,n=r===void 0?"":r,o=ya.some(function(i){return!!~n.indexOf(i)});o&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),Jn=function(e,t){for(var r=0,n=Object.keys(t);r0},e}(),Zn=typeof WeakMap!="undefined"?new WeakMap:new Bn,eo=function(){function e(t){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var r=Ea.getInstance(),n=new Ra(t,r,this);Zn.set(this,n)}return e}();["observe","unobserve","disconnect"].forEach(function(e){eo.prototype[e]=function(){var t;return(t=Zn.get(this))[e].apply(t,arguments)}});var ka=function(){return typeof nr.ResizeObserver!="undefined"?nr.ResizeObserver:eo}(),to=ka;var ro=new E,Ha=I(()=>H(new to(e=>{for(let t of e)ro.next(t)}))).pipe(x(e=>L(Te,H(e)).pipe(C(()=>e.disconnect()))),J(1));function de(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ge(e){return Ha.pipe(S(t=>t.observe(e)),x(t=>ro.pipe(_(({target:r})=>r===e),C(()=>t.unobserve(e)),l(()=>de(e)))),N(de(e)))}function bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function ar(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var no=new E,Pa=I(()=>H(new IntersectionObserver(e=>{for(let t of e)no.next(t)},{threshold:0}))).pipe(x(e=>L(Te,H(e)).pipe(C(()=>e.disconnect()))),J(1));function sr(e){return Pa.pipe(S(t=>t.observe(e)),x(t=>no.pipe(_(({target:r})=>r===e),C(()=>t.unobserve(e)),l(({isIntersecting:r})=>r))))}function oo(e,t=16){return dt(e).pipe(l(({y:r})=>{let n=de(e),o=bt(e);return r>=o.height-n.height-t}),Y())}var cr={drawer:V("[data-md-toggle=drawer]"),search:V("[data-md-toggle=search]")};function io(e){return cr[e].checked}function qe(e,t){cr[e].checked!==t&&cr[e].click()}function je(e){let t=cr[e];return b(t,"change").pipe(l(()=>t.checked),N(t.checked))}function $a(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ia(){return L(b(window,"compositionstart").pipe(l(()=>!0)),b(window,"compositionend").pipe(l(()=>!1))).pipe(N(!1))}function ao(){let e=b(window,"keydown").pipe(_(t=>!(t.metaKey||t.ctrlKey)),l(t=>({mode:io("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),_(({mode:t,type:r})=>{if(t==="global"){let n=_e();if(typeof n!="undefined")return!$a(n,r)}return!0}),fe());return Ia().pipe(x(t=>t?R:e))}function Me(){return new URL(location.href)}function ot(e){location.href=e.href}function so(){return new E}function co(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)co(e,r)}function M(e,t,...r){let n=document.createElement(e);if(t)for(let o of Object.keys(t))typeof t[o]!="undefined"&&(typeof t[o]!="boolean"?n.setAttribute(o,t[o]):n.setAttribute(o,""));for(let o of r)co(n,o);return n}function fr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function fo(){return location.hash.substring(1)}function uo(e){let t=M("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Fa(){return b(window,"hashchange").pipe(l(fo),N(fo()),_(e=>e.length>0),J(1))}function po(){return Fa().pipe(l(e=>se(`[id="${e}"]`)),_(e=>typeof e!="undefined"))}function Nr(e){let t=matchMedia(e);return Zt(r=>t.addListener(()=>r(t.matches))).pipe(N(t.matches))}function lo(){let e=matchMedia("print");return L(b(window,"beforeprint").pipe(l(()=>!0)),b(window,"afterprint").pipe(l(()=>!1))).pipe(N(e.matches))}function qr(e,t){return e.pipe(x(r=>r?t():R))}function ur(e,t={credentials:"same-origin"}){return ve(fetch(`${e}`,t)).pipe(ce(()=>R),x(r=>r.status!==200?Tt(()=>new Error(r.statusText)):H(r)))}function Ue(e,t){return ur(e,t).pipe(x(r=>r.json()),J(1))}function mo(e,t){let r=new DOMParser;return ur(e,t).pipe(x(n=>n.text()),l(n=>r.parseFromString(n,"text/xml")),J(1))}function pr(e){let t=M("script",{src:e});return I(()=>(document.head.appendChild(t),L(b(t,"load"),b(t,"error").pipe(x(()=>Tt(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(l(()=>{}),C(()=>document.head.removeChild(t)),Oe(1))))}function ho(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function bo(){return L(b(window,"scroll",{passive:!0}),b(window,"resize",{passive:!0})).pipe(l(ho),N(ho()))}function vo(){return{width:innerWidth,height:innerHeight}}function go(){return b(window,"resize",{passive:!0}).pipe(l(vo),N(vo()))}function yo(){return Q([bo(),go()]).pipe(l(([e,t])=>({offset:e,size:t})),J(1))}function lr(e,{viewport$:t,header$:r}){let n=t.pipe(X("size")),o=Q([n,r]).pipe(l(()=>Be(e)));return Q([r,t,o]).pipe(l(([{height:i},{offset:a,size:s},{x:f,y:c}])=>({offset:{x:a.x-f,y:a.y-c+i},size:s})))}(()=>{function e(n,o){parent.postMessage(n,o||"*")}function t(...n){return n.reduce((o,i)=>o.then(()=>new Promise(a=>{let s=document.createElement("script");s.src=i,s.onload=a,document.body.appendChild(s)})),Promise.resolve())}var r=class{constructor(n){this.url=n,this.onerror=null,this.onmessage=null,this.onmessageerror=null,this.m=a=>{a.source===this.w&&(a.stopImmediatePropagation(),this.dispatchEvent(new MessageEvent("message",{data:a.data})),this.onmessage&&this.onmessage(a))},this.e=(a,s,f,c,u)=>{if(s===this.url.toString()){let p=new ErrorEvent("error",{message:a,filename:s,lineno:f,colno:c,error:u});this.dispatchEvent(p),this.onerror&&this.onerror(p)}};let o=new EventTarget;this.addEventListener=o.addEventListener.bind(o),this.removeEventListener=o.removeEventListener.bind(o),this.dispatchEvent=o.dispatchEvent.bind(o);let i=document.createElement("iframe");i.width=i.height=i.frameBorder="0",document.body.appendChild(this.iframe=i),this.w.document.open(),this.w.document.write(` + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Contribute

+ +

🔧 Pull Request Steps

+

This project is open source, so you can create a pull request(PR) after you fix issues. Get a local copy of the plugins checked out for development using the following process.

+

Pull Request

+

Before uploading your PR, run test one last time to check if there are any errors. If it has no errors, commit and then push it!

+

For more information on PR's steps, please see links in the Contributing section.

+

Commit messages

+

Please make this project more fun and easy to scan by using emoji prefixes for your +commit messages (see GitMoji).

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Commit typeEmoji
Initial commit🎉 :tada:
Version tag🔖 :bookmark:
New feature:sparkles:
Bugfix🐛 :bug:
Metadata📇 :card_index:
Documentation📚 :books:
Documenting source code💡 :bulb:
Performance🐎 :racehorse:
Cosmetic💄 :lipstick:
Tests🚨 :rotating_light:
Adding a test:white_check_mark:
Make a test pass✔️ :heavy_check_mark:
General update⚡️ :zap:
Improve format/structure🎨 :art:
Refactor code🔨 :hammer:
Removing code/files🔥 :fire:
Continuous Integration💚 :green_heart:
Security🔒 :lock:
Upgrading dependencies⬆️ :arrow_up:
Downgrading dependencies⬇️ :arrow_down:
Lint👕 :shirt:
Translation👽 :alien:
Text📝 :pencil:
Critical hotfix🚑 :ambulance:
Deploying stuff🚀 :rocket:
Fixing on MacOS🍎 :apple:
Fixing on Linux🐧 :penguin:
Fixing on Windows🏁 :checkered_flag:
Work in progress🚧 :construction:
Adding CI build system👷 :construction_worker:
Analytics or tracking code📈 :chart_with_upwards_trend:
Removing a dependency:heavy_minus_sign:
Adding a dependency:heavy_plus_sign:
Docker🐳 :whale:
Configuration files🔧 :wrench:
Package.json in JS📦 :package:
Merging branches🔀 :twisted_rightwards_arrows:
Bad code / need improv.💩 :hankey:
Reverting changes:rewind:
Breaking changes💥 :boom:
Code review changes👌 :ok_hand:
Accessibility♿️ :wheelchair:
Move/rename repository🚚 :truck:
OtherBe creative
+

💬 Contributing

+ + + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/credits/credits/index.html b/credits/credits/index.html new file mode 100644 index 0000000..33ddfa9 --- /dev/null +++ b/credits/credits/index.html @@ -0,0 +1,1101 @@ + + + + + + + + + + + + + + + + + + + + + + + + Credits - QGIS Animation Workbench + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Credits

+ +

Author

+

This plugin was developed by:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Tim SuttonNyall DawsonJeremy Prior
TimNyall DawsonJeremy
Coder and Ideas GuyGenius Guru of AwesomenessDocument and Logo Guy
timlinux @ githubnyalldawson @ githubJeremy-Prior @ github
+

Contributors

+

Thanks to:

+
    +
  • Mathieu Pellerin (@nirvn)
  • +
  • Thiasha Vythilingam (@ThiashaV)
  • +
+

We are looking for contributors, add yourself here!

+

Also:

+ + + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/develop/design/index.html b/develop/design/index.html new file mode 100644 index 0000000..a8e0795 --- /dev/null +++ b/develop/design/index.html @@ -0,0 +1,1009 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Design - QGIS Animation Workbench + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Design

+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/develop/docs/index.html b/develop/docs/index.html new file mode 100644 index 0000000..eec7da7 --- /dev/null +++ b/develop/docs/index.html @@ -0,0 +1,1064 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Documentation - QGIS Animation Workbench + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Working with documentation

+

Documentation is written using mkdocs.

+

Building documentation PDF

+

You can build a copy of the documentation as a PDF file using the following steps:

+
pip install mkdocs-with-pdf
+pip install mkdocs-material
+pip install qrcode
+mkdocs build --config-file mkdocs-pdf.yml
+xdg-open pdfs/QGISAnimationWorkbench.pdf 
+
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/develop/setup/index.html b/develop/setup/index.html new file mode 100644 index 0000000..dd4d884 --- /dev/null +++ b/develop/setup/index.html @@ -0,0 +1,1131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Setup - QGIS Animation Workbench + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Developer Notes

+

Setup

+

Fork main branch into your personal repository. Clone it to local computer. Install QGIS and the following dependencies.

+
    +
  • debugpy
  • +
  • convert (imagemagick)
  • +
  • ffmpeg
  • +
  • vscode (dont use flatpak, debugging will not work with QGIS)
  • +
+

Before starting development, you should check if there are any errors.

+
git clone https://github.com/{your-personal-repo}/QGISAnimationWorkbench.git
+ln -s QGISAnimationWorkbench ~.local/share/QGIS/QGIS3/profiles/<profile>/python/plugins
+
+

Enable the python in the QGIS plugin manager. You should also install the Plugin Reloader plugin so you can quickly deploy changes to your local session in QGIS as you are working.

+

Debugging

+
TODO
+
+

Packaging

+
TODO
+
+

Run test

+
TODO
+
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/faq/faq/index.html b/faq/faq/index.html new file mode 100644 index 0000000..7ebf606 --- /dev/null +++ b/faq/faq/index.html @@ -0,0 +1,1138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + FAQ - QGIS Animation Workbench + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + + +

Frequently Asked Questions

+

Can I add any image to the intro or outro?

+
    +
  • As long as you can provide the proper attribution for an image you can use it in your project.
  • +
+

I have an older, less powerful, computer, will it handle running this workbench?

+
    +
  • If you open the standard QGIS settings dialog and select the Animation Workbench options +you can follow the advice with regards to lowering the number of threads allowed during +rendering to help you computer cope. Rendering shorter movies or GIFs (i.e. fewer frames) +will also help. Below is an example of running a job with 9000 frames at 60fps and 999 +frames per feature
  • +
+

imagem

+

And the subsequent CPU load during processing:

+

cpu

+

After processing:

+

imagem

+

And here is the resulting video:

+

https://youtu.be/1quc3xPdJsU

+

I get an error when rendering because of my intro / outro images

+

Currently your filenames should not contain spaces or special characters (e.g.(, ), [, ], {, }, <, >, /, \, :, *, ?, |, ", &, etc.).

+

Can I use a movie as the intro / outro media?

+

This is planned but not yet implemented. Tim - check.

+

Can I pay you to add some features?

+

This is a fun / hobby project, currently we want other contributors who also want to +have a fun experience with building this plugin and contribute in-kind efforts to the +project. Both Kartoza and North-Road +offer commercial development services but not for this plugin which is a intended to +provide an experimental, no-pressire space for us to work on something fun for QGIS.

+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..bce4312 --- /dev/null +++ b/index.html @@ -0,0 +1,1084 @@ + + + + + + + + + + + + + + + + + + + + + + + + QGIS Animation Workbench + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

QGIS Animation Workbench

+

Welcome to the QGIS Animation Workbench (QAW). QAW is a QGIS Plugins that will help you bring your maps to life! Let's start with a quick overview. Click on the image below to view a 14 minute walkthrough on YouTube.

+

Overview

+

🤖 Why QGIS Animation Workbench?

+

QGIS Animation Bench exists because we wanted to use all the awesome cartography features in QGIS and make cool, animated maps! +QGIS already includes the Temporal Manager which allows you to produce animations for time-based data. But what if you want to +make animations where you travel around the map, zooming in and out, and perhaps making features on the map wiggle and jiggle as the +animation progresses? That is what the animation workbench tries to solve...

+

🎨 Features

+
    +
  • Modes : Supports 3 modes: Sphere, Planar and Static.
  • +
  • Sphere: Creates a spinning globe effect. Like Google Earth might do, but with your own data and cartography.
  • +
  • Planar: Lets you move from feature to feature on a flat map, pausing at each if you want to.
  • +
  • Static: The frame of reference stays the same and you can animate the symbology within that scene.
  • +
  • Add music to your exported videos - see the Creative Commons website for a list of places where you can download free music (make sure it does not have a 'No Derivative Works' license).
  • +
  • Multithreaded, efficient rendering workflow. The plugin is designed to work well even on very modest hardware. If you have a fast PC, you can crank up the size to the thread pool to process more jobs at the same time.
  • +
+
+

Supports only English currently - we may add other languages in the future if there is demand.

+
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/library/img/animated-rotating-symbol.gif b/library/img/animated-rotating-symbol.gif new file mode 100644 index 0000000..75362ce Binary files /dev/null and b/library/img/animated-rotating-symbol.gif differ diff --git a/library/img/flying-points.gif b/library/img/flying-points.gif new file mode 100644 index 0000000..455d575 Binary files /dev/null and b/library/img/flying-points.gif differ diff --git a/library/img/make-line.png b/library/img/make-line.png new file mode 100644 index 0000000..4d16f7d Binary files /dev/null and b/library/img/make-line.png differ diff --git a/library/img/marker-offset.png b/library/img/marker-offset.png new file mode 100644 index 0000000..89240d0 Binary files /dev/null and b/library/img/marker-offset.png differ diff --git a/library/img/rotated-symbol-properties.png b/library/img/rotated-symbol-properties.png new file mode 100644 index 0000000..c7e7986 Binary files /dev/null and b/library/img/rotated-symbol-properties.png differ diff --git a/library/img/rotated-symbol.png b/library/img/rotated-symbol.png new file mode 100644 index 0000000..9253113 Binary files /dev/null and b/library/img/rotated-symbol.png differ diff --git a/library/snippets/index.html b/library/snippets/index.html new file mode 100644 index 0000000..cf81693 --- /dev/null +++ b/library/snippets/index.html @@ -0,0 +1,1267 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Expression Examples - QGIS Animation Workbench + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Snippets

+

🌏 QGIS Support

+

Should work with and version of QGIS 3.x. If you have QGIS 3.26 or better you can benefit from the animated icon support (see @nyalldawson's most excellent patch #48060).

+

For QGIS versions below 3.26, you can animate markers by unpacking a GIF image into its constituent frames and then referencing a specific frame from the symbol data defined property for the image file. Note that to do this extraction below you need to have the Open Source ImageMagick application installed:

+

First extract a gif to a sequence of images:

+
convert cat.gif -coalesce cat_%05d.png
+
+

Example of how to create a dynamically changing image marker based on the current frame count:

+
@project_home 
+||
+'/gifs/cat_000'
+|| 
+lpad(to_string( @frame_number % 48 ), 2, '0')
+|| 
+'.png'
+
+

Note that for the above, 48 is the number of frames that the GIF was composed of, and it assumes the frames are in the project directory in a subfolder called gifs.

+

Line of travel

+

In this example we use a geometry generator to create a line between the origin point and the destination point:

+
if (@from_feature_id = $id OR @to_feature_id = $id,
+ -- read this from inside to out so 
+ -- last tranform the geometry back to the map crs
+ transform( 
+  -- densify the geometry so that when we transform
+  -- back it makes a great circle
+  densify_by_count(  
+   -- move the geometry into a crs that 
+   -- shows a great circle as a straight line
+   transform( 
+    -- make a line from the previous pont to the next point
+    make_line( 
+     geometry(@from_feature), 
+     geometry(@to_feature)
+    ),  
+    @map_crs, 'EPSG:4326'),
+   99), 
+  'EPSG:4326',  @map_crs),
+ None)
+
+

Example output

+ +

Showing diagnostic information in the QGIS copyright label:

+
[%
+'Feature Variables:' ||
+' \n------------------------' ||
+' \nPrevious Feature ' || to_string(coalesce(attribute(@previous_feature, 'name'), '-'))  ||
+' \nPrevious Feature ID ' || to_string(coalesce(@previous_feature_id, '-'))  ||
+' \n' ||
+' \nNext Feature ' || to_string(coalesce(attribute(@next_feature, 'name'), '-'))  ||
+' \nNext Feature ID ' || to_string(coalesce(@next_feature_id, '-'))  ||
+' \n' ||
+' \nHover Feature ' || to_string(coalesce(attribute(@hover_feature, 'name'), '-'))  ||
+' \nHover Feature ID ' || to_string(coalesce(@hover_feature_id, '-'))  ||
+' \n' ||
+' \nFrom Feature ' || to_string(coalesce(attribute(@from_feature, 'name'), '-'))  ||
+' \nFrom Feature ID ' || to_string(coalesce(@from_feature_id, '-'))  ||
+' \n' ||
+' \nTo Feature ' || to_string(coalesce(attribute(@to_feature, 'name'), '-'))  ||
+' \nTo Feature ID ' || to_string(coalesce(@to_feature_id, '-'))  ||
+' \n' ||
+' \nTotal Hover Frames ' || to_string(coalesce(@hover_frames, 0))  ||
+' \nCurrent Hover Frame ' || to_string(coalesce(@current_hover_frame, 0))  ||
+' \nTotal Travel Frames ' || to_string(coalesce(@travel_frames, 0))  ||
+' \nCurrent Travel Frame ' || to_string(coalesce(@current_travel_frame, 0))  ||
+' \nTotal Frame Count ' || to_string(coalesce(@total_frame_count, 0))  ||
+' \nFrame Number ' || to_string(coalesce(@frame_number, 0))  ||
+' \nFrame Rate ' || to_string(coalesce(@frame_rate, 0))  ||
+' \nwith Current Animation Action: ' || @current_animation_action ||
+' \nTo Direction ' ||  coalesce(format_number(degrees(azimuth( geometry(@hover_feature), geometry(@previous_feature) ) ) ), 0) || 
+' \nFrom Direction ' ||  coalesce(format_number(degrees( azimuth( geometry(@hover_feature), geometry(@next_feature) ) ) ), 0)
+%]
+
+
+
+

Example output:

+

copyright-label

+

Variable size of labels

+

Variably changing the size on a label as we approach it in the animation:

+

```40 * ((@frame_number % @hover_frames) / @hover_frames)

+

+## Calculating the angle between points
+
+You can calculate the angle between the hover point and the previous point like this:
+
+```python
+coalesce(
+ format_number(
+  degrees( 
+   azimuth( 
+    geometry(@hover_feature), 
+    geometry(@previous_feature) 
+   )
+  )
+ ), 0)
+
+

Rotation

+

You can set the angle of rotation for a symbol using this expression:

+

Rotated Symbol Preview

+

Rotated Symbol Preview

+

Using this technique you can also create an animation effect showing the source +direction of travel and the new destination.

+
scale_linear (
+ @current_hover_frame,
+ 0,
+ @hover_frames,
+ degrees( 
+  azimuth( 
+   geometry(@hover_feature), 
+   geometry(@previous_feature) 
+  )
+ ),
+ degrees( 
+  azimuth( 
+   geometry(@hover_feature), 
+   geometry(@next_feature) 
+  )
+ )
+)
+
+

Will produce something like this:

+

Animated Rotated Symbol Preview

+

Flying points cluster

+

Here is an example where we animate all the points in a cluster that are not the hover point. We use an easing function to make the animation have an interesting circular motion.

+

Animated Flying Points Symbol Preview

+
-- Taken from https://spicyyoghurt.com/tools/easing-functions
+--    t = Time - Amount of time that has passed since the beginning of the animation. Usually starts at 0 and is slowly increased using a game loop or other update function.
+--    b = Beginning value - The starting point of the animation. Usually it's a static value, you can start at 0 for example.
+--    c = Change in value - The amount of change needed to go from starting point to end point. It's also usually a static value.
+--    d = Duration - Amount of time the animation will take. Usually a static value aswell.
+-- Sinusoidal
+-- -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
+
+-- Use with the animation in static mode
+if(@hover_feature_id != $id,
+array(
+  (-@hover_frames / 2) * (cos( (pi() * @frame_number / @hover_frames ) - 1)) ,
+ (-@hover_frames / 2) * (sin( (pi() * @frame_number / @hover_frames ) - 1)) 
+),
+array (0,0))
+
+
+

This function should be applied to the offset X,Y property of the symbol.

+

Offset

+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/library/variables/index.html b/library/variables/index.html new file mode 100644 index 0000000..2128b9c --- /dev/null +++ b/library/variables/index.html @@ -0,0 +1,1308 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Expression Variables - QGIS Animation Workbench + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

🧮 QGIS Expression Variables

+

The animation workbench exposes or modifies a number of different QGIS Expression variables that you can use to achieve different dynamic rendering effects.

+

Common variables

+

These variables will always be available, regardless of the animation mode

+ + + + + + + + + + + + + + + + + + + + + +
VariableNotes
frame_numberFrame number within the current dwell or pan range.
frame_rateNumber of frames per second that the video will be rendered at.
total_frame_countTotal number of frames for the whole animation across all features.
+

Fixed extent mode variables (with layer)

+

These variables are available when in the fixed extent animation mode when a vector layer has been set

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VariableNotes
hover_featureThe feature we are currently hovering over
hover_feature_idFeature ID for the feature we a current hovering over
previous_featureThe previously visited feature (or NULL if there isn't one)
previous_feature_idFeature ID for the previously visited feature (or NULL if there isn't one)
next_featureThe next feature to visit after the current one (or NULL if there isn't one)
next_feature_idFeature ID for the next feature to visit after the current one (or NULL if there isn't one)
current_hover_frameThe frame number for the current feature (i.e. how many frames we have hovered at the current feature)
hover_framesNumber of frames we will hover at the current feature for
current_animation_actionAlways "Hovering"
+

Planar/Sphere modes

+

These variables are available in the Planar or Sphere mode.

+ + + + + + + + + + + + + +
VariableNotes
current_animation_actionEither "Hovering" or "Travelling"
+

When hovering

+

These variables are available in planar or sphere mode, when the animation is currently hovering over a feature

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VariableNotes
hover_featureThe feature we are currently hovering over
hover_feature_idThe feature ID for the feature we are currently hovering over
previous_featureThe previously visited feature (or NULL if there isn't one)
previous_feature_idFeature ID for the previously visited feature (or NULL if there isn't one)
next_featureThe next feature to visit after the current one (or NULL if there isn't one)
next_feature_idFeature ID for the next feature to visit after the current one (or NULL if there isn't one)
current_hover_frameThe frame number for the current feature (i.e. how many frames we have hovered at the current feature)
hover_framesNumber of frames we will hover at the current feature for
+

When travelling

+

These variables are available in planar or sphere mode, when the animation is currently travelling between two features

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VariableNotes
from_featureThe feature we are travelling away from
from_feature_idThe feature ID for the feature we are travelling away from
to_featureThe feature we are heading toward
to_feature_idThe feature ID for the feature we are heading toward
current_travel_frameThe frame number for the current travel operation
travel_framesNumber of frames we will travel between the current features
+

Example expressions

+

Visit the snippets section of our documentation for example expressions.

+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/manual/img/001_AnimationPlan_SpherePlanar_1.png b/manual/img/001_AnimationPlan_SpherePlanar_1.png new file mode 100644 index 0000000..967fda0 Binary files /dev/null and b/manual/img/001_AnimationPlan_SpherePlanar_1.png differ diff --git a/manual/img/002_AnimationPlan_FixedExtent_1.png b/manual/img/002_AnimationPlan_FixedExtent_1.png new file mode 100644 index 0000000..adcde67 Binary files /dev/null and b/manual/img/002_AnimationPlan_FixedExtent_1.png differ diff --git a/manual/img/003_IntroTab_1.png b/manual/img/003_IntroTab_1.png new file mode 100644 index 0000000..17cf806 Binary files /dev/null and b/manual/img/003_IntroTab_1.png differ diff --git a/manual/img/004_OutroTab_1.png b/manual/img/004_OutroTab_1.png new file mode 100644 index 0000000..a8529a1 Binary files /dev/null and b/manual/img/004_OutroTab_1.png differ diff --git a/manual/img/005_SoundtrackTab_1.png b/manual/img/005_SoundtrackTab_1.png new file mode 100644 index 0000000..88d6560 Binary files /dev/null and b/manual/img/005_SoundtrackTab_1.png differ diff --git a/manual/img/006_OutputTab_1.png b/manual/img/006_OutputTab_1.png new file mode 100644 index 0000000..2ce8164 Binary files /dev/null and b/manual/img/006_OutputTab_1.png differ diff --git a/manual/img/007_ProgressTab_1.png b/manual/img/007_ProgressTab_1.png new file mode 100644 index 0000000..eb74433 Binary files /dev/null and b/manual/img/007_ProgressTab_1.png differ diff --git a/manual/img/008_NewProject_1.png b/manual/img/008_NewProject_1.png new file mode 100644 index 0000000..47595d3 Binary files /dev/null and b/manual/img/008_NewProject_1.png differ diff --git a/manual/img/009_AddLayers_1.png b/manual/img/009_AddLayers_1.png new file mode 100644 index 0000000..e35ebc6 Binary files /dev/null and b/manual/img/009_AddLayers_1.png differ diff --git a/manual/img/010_AddFeatures_1.png b/manual/img/010_AddFeatures_1.png new file mode 100644 index 0000000..0e35602 Binary files /dev/null and b/manual/img/010_AddFeatures_1.png differ diff --git a/manual/img/011_LayersList_10.png b/manual/img/011_LayersList_10.png new file mode 100644 index 0000000..1a15101 Binary files /dev/null and b/manual/img/011_LayersList_10.png differ diff --git a/manual/img/012_StyledLayers_1.png b/manual/img/012_StyledLayers_1.png new file mode 100644 index 0000000..2b5e803 Binary files /dev/null and b/manual/img/012_StyledLayers_1.png differ diff --git a/manual/img/013_FishAnimation_1.png b/manual/img/013_FishAnimation_1.png new file mode 100644 index 0000000..fdb71b2 Binary files /dev/null and b/manual/img/013_FishAnimation_1.png differ diff --git a/manual/img/014_EditExpression_1.png b/manual/img/014_EditExpression_1.png new file mode 100644 index 0000000..959a3dd Binary files /dev/null and b/manual/img/014_EditExpression_1.png differ diff --git a/manual/img/015_FishExpression_1.png b/manual/img/015_FishExpression_1.png new file mode 100644 index 0000000..3cb8421 Binary files /dev/null and b/manual/img/015_FishExpression_1.png differ diff --git a/manual/img/016_OpenAW_1.png b/manual/img/016_OpenAW_1.png new file mode 100644 index 0000000..81ed383 Binary files /dev/null and b/manual/img/016_OpenAW_1.png differ diff --git a/manual/img/017_AnimationPlan_1.png b/manual/img/017_AnimationPlan_1.png new file mode 100644 index 0000000..61e8877 Binary files /dev/null and b/manual/img/017_AnimationPlan_1.png differ diff --git a/manual/img/018_Output_1.png b/manual/img/018_Output_1.png new file mode 100644 index 0000000..327f720 Binary files /dev/null and b/manual/img/018_Output_1.png differ diff --git a/manual/img/manual_output.gif b/manual/img/manual_output.gif new file mode 100644 index 0000000..236afc5 Binary files /dev/null and b/manual/img/manual_output.gif differ diff --git a/manual/project_preparation/index.html b/manual/project_preparation/index.html new file mode 100644 index 0000000..052120d --- /dev/null +++ b/manual/project_preparation/index.html @@ -0,0 +1,1106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Preparing your project - QGIS Animation Workbench + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

How to set up a project to work with the animation plugin

+

1. The first step for getting an output using the Workbench is to create a QGIS Project +  +Open QGIS and click on ProjectNew

+

New Project

+

 

+

Next, add new layers to your project. You will want a few layers; one, or more, backing +layer(s) (vector layers or XYZ Tiles), a layer for the workbench to follow, and one, +or more, layer(s) of animated points. The example in this section only has one animated +layer. + 

+

To add a layer, go to LayerCreate Layer and then select the type of layer you +want to add. The example adds a point layer to a GeoPackage to make the project more +portable.

+

Add Layers

+

Once you have added your layers you need to add features to the layers. This is done +by selecting a layer and then clicking Toggle Editing (1) ➔ Add PointFeature (2). +Then click around on your map to add as few, or as many, features as you need.

+

Add Features

+

The example project has four layers: two point layers (3) and two backing layers (4).

+

Layer List

+
+

A simple way to add a vector base layer is to type "world" into the coordinate +textbox

+
+

Finally, style your layers to make your project look aesthetically pleasing. To +style your layers you must select the layer you want to style and then using the +Layer Styling toolbar, play around with the style of the layer until it suits you. A +good practice is to have your backing layers as more muted colours and your desired +features as more eye-catching colours.

+

Style Layers

+

  +You now have a QGIS Project. + 

+

2. The next step is to choose which features you want to be animated. + 

+

Pick the layer (or layers) that you want to have animations. Then either find, or create, +the animation for the layer. Make sure you have all the correct attribution for any +animations you use. Below is an example of a simple fish animation split into its frames. +The frames are repeated to slow down the animation's playback speed.

+

Fish Animation Frames

+
    +
  • Now use the QGIS Expressions system with the variables introduced by the Animation +Workbench to define behaviours of your symbols during flight and hover modes of your +animation. +  +Select the layer you want to animate and open the Layer Styling toolbar.
  • +
+
+

If you are using QGIS 3.26 you can simply use the new animated point symbol, +or if you're using an older version of QGIS 3.x follow the instructions below.

+
+

The layer should contain a Raster Image Marker. Once you have selected the marker you +want to use click on the QGIS Expressions dropdown menu (5) and click on Edit (6).

+

Edit Expression

+

 

+
+

You can also make a marker move along a line relative to the frame of the animation. +Use the Code Snippets Section for more in-depth help.

+
+

The example below works with the animation from earlier.

+

Expression Snippet

+
@project_home
+|| 
+'/fish/fish_00'
+||
+lpad(to_string( @frame_number % 32), 2, '0')
+||
+'.png'
+
+

3. Configure your animation

+

After animating your markers it's time to configure your animation. Open the Animated +Workbench and begin choosing between the different modes and options. + 

+

Open the Workbench by clicking the Animation Workbench (7) icon in the Plugin Toolbar.

+

Open Workbench

+

Configure the settings for your animation. The screenshot below is configured for +the example presented in this section. The Animation Layer is selected as route (8) +because that is the path that the output animation will fly along. The Zoom Range (9) was +selected from the Map Canvas Extent, and the Frame rate per second (fps) (10) was set to +match the number of frames of the animated markers so that they will play nicely in +the output. The other settings were selected as a personal choice.

+

Animation Plan

+

Select the Output Resolution (11) and a location for your output by clicking on the +ellipsis (three dots) or by typing in the desired file path (12).

+

Output Location

+
+

Refer to the Workbench User Interface section for more information about +what various settings and buttons accomplish.

+
+

4. Render your animation! +  +Click Run and render your output. The output below is the output from the example.

+

Output GIF

+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/manual/under_the_hood/index.html b/manual/under_the_hood/index.html new file mode 100644 index 0000000..a0faf88 --- /dev/null +++ b/manual/under_the_hood/index.html @@ -0,0 +1,1054 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Under the hood - QGIS Animation Workbench + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

What is the Workbench doing?

+
    +
  • +

    What does the workbench do?

    +

    The workbench creates animations from QGIS by generating multiple static frames (images) +and then combining those frames into an animation. The user tells QGIS how the frames +should change from one to the other. In QGIS 3.26 and later the animated markers +allow markers to be animated without the use of the expressions system. + 

    +
  • +
  • +

    How do the animated markers work?

    +

    In the code snippet below, the user tells QGIS that as the frame count increments by +one the Raster Image Marker should change to the next image in the sequence.

    +

    Code Snippet

    +

    py + @project_home + || + '/fish/fish_00' + || + lpad(to_string( @frame_number % 32), 2, '0') + || + '.png'

    +

    The user specifies the path of the image (@project_home/fish/fish_00). Then the +lpad(to_string( @frame_number % 32), 2, '0') tells QGIS to convert the frame +number to a string and then modulus the number of frames by the number of animation +frames (32) (i.e. QGIS divides the number of frames by 32 and then repeats the +sequence when the remainder is zero). The 2 and '0' in the snippet tell +QGIS to pad the /fish/fish_00 with two zeroes at the end. Finally the '.png' tells +QGIS the type of file to finish off the path. + 

    +
  • +
  • +

    Frame Output location on Windows

    +

    For users on a Windows machine who are interested in seeing the frames before they +are combined into an animation (GIF or movie) you can find them by going to +"C:\Users\Username\AppData\Local\Temp\animation_workbench-0000000000.png". Bear in +mind that AppData is a hidden file, so it's preferable to not make changes unless +explicitly told otherwise. + 

    +
  • +
  • +

    Frame Output on Linux

    +

    The frames should be in your /tmp directory.

    +
  • +
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/manual/workbench_ui/index.html b/manual/workbench_ui/index.html new file mode 100644 index 0000000..3f2acf5 --- /dev/null +++ b/manual/workbench_ui/index.html @@ -0,0 +1,1321 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + The workbench user interface - QGIS Animation Workbench + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

The Workbench User Interface

+

Animation Plan

+

Sphere and Planar

+
    +
  • Render Modes (1): These determine the behaviour and type of animation
  • +
  • Sphere: The coordinate reference system (CRS) will be manipulated to create a + spinning globe effect. Like Google Earth might do, but with your own data and + cartography.
  • +
  • Planar: The coordinate reference system (CRS) will not be altered, but the camera + will pan and zoom to each point. It lets you move from feature to feature on a + flat map, pausing at each if you want to.
  • +
  • +

    Fixed extent: The frame of reference stays the same and you can animate the + symbology within that scene.

    +
  • +
  • +

    Animation Layer (2):

    +
  • +
  • Dropdown menu: This allows you to select which map layer you want the animation + to follow.
  • +
  • +

    Loop from final feature back to first feature: allows for a seamlessly looping + output GIF or movie(MP4).

    +
  • +
  • +

    Zoom Range (3): The scale range that the animation should move through.

    +
  • +
  • Minimum (exclusive): The zenith (highest point) of the animation when it zooms out + while travelling between points, i.e. the most "zoomed out".
  • +
  • +

    Maximum (inclusive): The scale (zoom level) used when we arrive at each point, + i.e. the most "zoomed in".

    +
  • +
  • +

    Data defined settings (4)

    +
  • +
  • +

    Scale

    +
      +
    • Minimum: User-defined minimum scale
    • +
    • Maximum: User-defined maximum scale
    • +
    +
  • +
  • +

    Animation Frames (5)

    +
  • +
  • Frame rate per second (fps): When writing to video or gif, how many frames per + second to use.
  • +
  • Travel Duration: This is the number of seconds that the animation will take during + animation from one feature to the next.
  • +
  • Feature Hover duration: This is the number of seconds that the animation will hover + over each feature.
  • +
+

Fixed Extent

+
    +
  • Extent (6):
  • +
  • Can be manually entered using North, East, South, and West coordinates as limits.
  • +
  • Can be calculated from a map layer, the layout map, or a bookmark.
  • +
  • Can be set to match the Map Canvas Extent
  • +
  • +

    Can be set as a rectangular extent using the Draw on Canvas feature.

    +
  • +
  • +

    Pan and Zoom Easings (7)

    +
  • +
  • What are Easings: Easings are transitions from one state to another along a smooth + curve. A user can specify the shape of the curve used.
  • +
  • Pan Easings (XY): The pan easing will determine the motion characteristics of the + camera on the X and Y axis as it flies across the scene (i.e. how it accelerates + or decelerates between points)
  • +
  • +

    Zoom Easing (Z): The pan easing will determine the motion characteristics of the + camera on the Z axis as it flies across the scene (i.e. how the camera zooms in + and out of the points)

    +
  • +
  • +

    Frame previews (8): A preview of what each frame of the animation will look like. A + user can decide which Frame to view.

    +
  • +
+

Intro Tab

+

Edit the intro section of the generated movie here.

+

Intro Tab

+
    +
  • Media: List of the various images or movies selected for the intro section. You can + drag and drop items in the list to change the play order.
  • +
  • Add Media (Plus sign) (1): Add images or movies
  • +
  • +

    Remove Media (Minus sign) (2): Remove images or movies

    +
  • +
  • +

    Duration (3): For images, you can set a duration for each image (in seconds).

    +
  • +
  • +

    Preview Frame (4): This shows what the media will look like.

    +
  • +
  • +

    Details: Provides details about where the media is stored on your computer.

    +
  • +
+

Outro Tab

+

Edit the outro section of the generated movie here.

+

Outro Tab

+
    +
  • Media: List of the various images or movies selected for the outro section. You can + drag and drop items in the list to change the play order.
  • +
  • Add Media (Plus sign) (1): Add images or movies
  • +
  • +

    Remove Media (Minus sign) (2): Remove images or movies

    +
  • +
  • +

    Duration (3): For images, you can set a duration for each image (in seconds).

    +
  • +
  • +

    Preview Frame (4): This shows what the media will look like.

    +
  • +
  • +

    Details: Provides details about where the media is stored on your computer.

    +
  • +
+

Soundtrack Tab

+

Soundtrack Tab

+
    +
  • Media: List of the various sound files (.mp3 or .wav) to play during the generated movie. + You can drag and drop items in the list to change the play order.
  • +
  • Add Media (Plus sign) (1): Add sound files (.mp3 or .wav) to play during the + generated movie.
  • +
  • +

    Remove Media (Minus sign) (2): Remove sound files (.mp3 or .wav)

    +
  • +
  • +

    Duration (3): The cumulative length of your soundtracks should be as long, or longer, + than your movie, including the intro/outro sections. If the soundtrack is longer + than the movie it will be truncated (shortened) when the movie ends.

    +
  • +
  • +

    Details: Provides details about where the media is stored on your computer.

    +
  • +
+

Output

+

Output Tab

+
    +
  • Output Options: Select which output format you would like. Regardless of the format chosen, + a folder of images will be created, one image per frame.
  • +
  • Re-use cached Images (1): This will not erase cached images on disk and will resume + processing from the last cached image.
  • +
  • Animated GIF (2): For this export to work, you need to have the ImageMagick 'convert' + application available on your system.
  • +
  • Movie (MP4) (3): For this option to work, you need to have the 'ffmpeg' application + on your system.
  • +
  • Output Resolution (4): Allows a user to specify one of four image resolutions + for the output animation. The numbers in brackets for the first three options represent + the width and height of the output in pixels (i.e. width x height), and the fourth + option matches the output's size to the size of the Map Canvas on the screen.
  • +
  • File selection (ellipsis) (5): This lets a user select the location where the output + will be stored.
  • +
+

Progress

+

Progress Tab

+
    +
  • Frame Preview (1): A preview of what each frame of the animation will look like. + It changes automatically as the workbench runs.
  • +
  • Progress (2): This provides a detailed look at what is happening while the workbench + runs.
  • +
  • Total Tasks: This number represents the total number of frames that will be generated + by the workbench.
  • +
  • Completed Tasks: The number of tasks that have completed being processed.
  • +
  • Remaining Features: The number of features from your animation layer that still need + to be processed.
  • +
  • Active Tasks: The number of tasks (threads) currently being run by the workbench
  • +
  • Features Complete: The number of tasks that have been processed by the workbench.
  • +
  • Logs (3): A detailed list of what steps the workbench is doing (a record of processing)
  • +
  • Progress Bar (4): A visual representation of the workbench's progression as a percentage.
  • +
+

Other Buttons

+
    +
  • Run: Starts the process of getting an output from the workbench. It is greyed out + until a user provides a destination for the output file.
  • +
  • Close: Closes the workbench.
  • +
  • Cancel: Ends the workbench processing at whatever point it has reached when the + button is pressed.
  • +
  • Help: Opens a link to the Animation Workbench documentation.
  • +
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/search/search_index.json b/search/search_index.json new file mode 100644 index 0000000..803a6a4 --- /dev/null +++ b/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"QGIS Animation Workbench","text":"

Welcome to the QGIS Animation Workbench (QAW). QAW is a QGIS Plugins that will help you bring your maps to life! Let's start with a quick overview. Click on the image below to view a 14 minute walkthrough on YouTube.

"},{"location":"#why-qgis-animation-workbench","title":"\ud83e\udd16 Why QGIS Animation Workbench?","text":"

QGIS Animation Bench exists because we wanted to use all the awesome cartography features in QGIS and make cool, animated maps! QGIS already includes the Temporal Manager which allows you to produce animations for time-based data. But what if you want to make animations where you travel around the map, zooming in and out, and perhaps making features on the map wiggle and jiggle as the animation progresses? That is what the animation workbench tries to solve...

"},{"location":"#features","title":"\ud83c\udfa8 Features","text":"
  • Modes : Supports 3 modes: Sphere, Planar and Static.
  • Sphere: Creates a spinning globe effect. Like Google Earth might do, but with your own data and cartography.
  • Planar: Lets you move from feature to feature on a flat map, pausing at each if you want to.
  • Static: The frame of reference stays the same and you can animate the symbology within that scene.
  • Add music to your exported videos - see the Creative Commons website for a list of places where you can download free music (make sure it does not have a 'No Derivative Works' license).
  • Multithreaded, efficient rendering workflow. The plugin is designed to work well even on very modest hardware. If you have a fast PC, you can crank up the size to the thread pool to process more jobs at the same time.

Supports only English currently - we may add other languages in the future if there is demand.

"},{"location":"contribute/contribute/","title":"Contribute","text":""},{"location":"contribute/contribute/#pull-request-steps","title":"\ud83d\udd27 Pull Request Steps","text":"

This project is open source, so you can create a pull request(PR) after you fix issues. Get a local copy of the plugins checked out for development using the following process.

"},{"location":"contribute/contribute/#pull-request","title":"Pull Request","text":"

Before uploading your PR, run test one last time to check if there are any errors. If it has no errors, commit and then push it!

For more information on PR's steps, please see links in the Contributing section.

"},{"location":"contribute/contribute/#commit-messages","title":"Commit messages","text":"

Please make this project more fun and easy to scan by using emoji prefixes for your commit messages (see GitMoji).

Commit type Emoji Initial commit \ud83c\udf89 :tada: Version tag \ud83d\udd16 :bookmark: New feature \u2728 :sparkles: Bugfix \ud83d\udc1b :bug: Metadata \ud83d\udcc7 :card_index: Documentation \ud83d\udcda :books: Documenting source code \ud83d\udca1 :bulb: Performance \ud83d\udc0e :racehorse: Cosmetic \ud83d\udc84 :lipstick: Tests \ud83d\udea8 :rotating_light: Adding a test \u2705 :white_check_mark: Make a test pass \u2714\ufe0f :heavy_check_mark: General update \u26a1\ufe0f :zap: Improve format/structure \ud83c\udfa8 :art: Refactor code \ud83d\udd28 :hammer: Removing code/files \ud83d\udd25 :fire: Continuous Integration \ud83d\udc9a :green_heart: Security \ud83d\udd12 :lock: Upgrading dependencies \u2b06\ufe0f :arrow_up: Downgrading dependencies \u2b07\ufe0f :arrow_down: Lint \ud83d\udc55 :shirt: Translation \ud83d\udc7d :alien: Text \ud83d\udcdd :pencil: Critical hotfix \ud83d\ude91 :ambulance: Deploying stuff \ud83d\ude80 :rocket: Fixing on MacOS \ud83c\udf4e :apple: Fixing on Linux \ud83d\udc27 :penguin: Fixing on Windows \ud83c\udfc1 :checkered_flag: Work in progress \ud83d\udea7 :construction: Adding CI build system \ud83d\udc77 :construction_worker: Analytics or tracking code \ud83d\udcc8 :chart_with_upwards_trend: Removing a dependency \u2796 :heavy_minus_sign: Adding a dependency \u2795 :heavy_plus_sign: Docker \ud83d\udc33 :whale: Configuration files \ud83d\udd27 :wrench: Package.json in JS \ud83d\udce6 :package: Merging branches \ud83d\udd00 :twisted_rightwards_arrows: Bad code / need improv. \ud83d\udca9 :hankey: Reverting changes \u23ea :rewind: Breaking changes \ud83d\udca5 :boom: Code review changes \ud83d\udc4c :ok_hand: Accessibility \u267f\ufe0f :wheelchair: Move/rename repository \ud83d\ude9a :truck: Other Be creative"},{"location":"contribute/contribute/#contributing","title":"\ud83d\udcac Contributing","text":"
  • Code of Conduct
  • Contributing Guideline
  • Commit Convention
  • Issue Guidelines
"},{"location":"credits/credits/","title":"Credits","text":""},{"location":"credits/credits/#author","title":"Author","text":"

This plugin was developed by:

Tim Sutton Nyall Dawson Jeremy Prior Coder and Ideas Guy Genius Guru of Awesomeness Document and Logo Guy timlinux @ github nyalldawson @ github Jeremy-Prior @ github"},{"location":"credits/credits/#contributors","title":"Contributors","text":"

Thanks to:

  • Mathieu Pellerin (@nirvn)
  • Thiasha Vythilingam (@ThiashaV)

We are looking for contributors, add yourself here!

Also:

  • NHN and Tui Editor for the great README which I based this one on.
"},{"location":"develop/design/","title":"Design","text":""},{"location":"develop/docs/","title":"Working with documentation","text":"

Documentation is written using mkdocs.

"},{"location":"develop/docs/#building-documentation-pdf","title":"Building documentation PDF","text":"

You can build a copy of the documentation as a PDF file using the following steps:

pip install mkdocs-with-pdf\npip install mkdocs-material\npip install qrcode\nmkdocs build --config-file mkdocs-pdf.yml\nxdg-open pdfs/QGISAnimationWorkbench.pdf \n
"},{"location":"develop/setup/","title":"Developer Notes","text":""},{"location":"develop/setup/#setup","title":"Setup","text":"

Fork main branch into your personal repository. Clone it to local computer. Install QGIS and the following dependencies.

  • debugpy
  • convert (imagemagick)
  • ffmpeg
  • vscode (dont use flatpak, debugging will not work with QGIS)

Before starting development, you should check if there are any errors.

git clone https://github.com/{your-personal-repo}/QGISAnimationWorkbench.git\nln -s QGISAnimationWorkbench ~.local/share/QGIS/QGIS3/profiles/<profile>/python/plugins\n

Enable the python in the QGIS plugin manager. You should also install the Plugin Reloader plugin so you can quickly deploy changes to your local session in QGIS as you are working.

"},{"location":"develop/setup/#debugging","title":"Debugging","text":"
TODO\n
"},{"location":"develop/setup/#packaging","title":"Packaging","text":"
TODO\n
"},{"location":"develop/setup/#run-test","title":"Run test","text":"
TODO\n
"},{"location":"faq/faq/","title":"Frequently Asked Questions","text":""},{"location":"faq/faq/#can-i-add-any-image-to-the-intro-or-outro","title":"Can I add any image to the intro or outro?","text":"
  • As long as you can provide the proper attribution for an image you can use it in your project.
"},{"location":"faq/faq/#i-have-an-older-less-powerful-computer-will-it-handle-running-this-workbench","title":"I have an older, less powerful, computer, will it handle running this workbench?","text":"
  • If you open the standard QGIS settings dialog and select the Animation Workbench options you can follow the advice with regards to lowering the number of threads allowed during rendering to help you computer cope. Rendering shorter movies or GIFs (i.e. fewer frames) will also help. Below is an example of running a job with 9000 frames at 60fps and 999 frames per feature

And the subsequent CPU load during processing:

After processing:

And here is the resulting video:

https://youtu.be/1quc3xPdJsU

"},{"location":"faq/faq/#i-get-an-error-when-rendering-because-of-my-intro-outro-images","title":"I get an error when rendering because of my intro / outro images","text":"

Currently your filenames should not contain spaces or special characters (e.g.(, ), [, ], {, }, <, >, /, \\, :, *, ?, |, \", &, etc.).

"},{"location":"faq/faq/#can-i-use-a-movie-as-the-intro-outro-media","title":"Can I use a movie as the intro / outro media?","text":"

This is planned but not yet implemented. Tim - check.

"},{"location":"faq/faq/#can-i-pay-you-to-add-some-features","title":"Can I pay you to add some features?","text":"

This is a fun / hobby project, currently we want other contributors who also want to have a fun experience with building this plugin and contribute in-kind efforts to the project. Both Kartoza and North-Road offer commercial development services but not for this plugin which is a intended to provide an experimental, no-pressire space for us to work on something fun for QGIS.

"},{"location":"library/snippets/","title":"Snippets","text":""},{"location":"library/snippets/#qgis-support","title":"\ud83c\udf0f QGIS Support","text":"

Should work with and version of QGIS 3.x. If you have QGIS 3.26 or better you can benefit from the animated icon support (see @nyalldawson's most excellent patch #48060).

For QGIS versions below 3.26, you can animate markers by unpacking a GIF image into its constituent frames and then referencing a specific frame from the symbol data defined property for the image file. Note that to do this extraction below you need to have the Open Source ImageMagick application installed:

First extract a gif to a sequence of images:

convert cat.gif -coalesce cat_%05d.png\n

Example of how to create a dynamically changing image marker based on the current frame count:

@project_home \n||\n'/gifs/cat_000'\n|| \nlpad(to_string( @frame_number % 48 ), 2, '0')\n|| \n'.png'\n

Note that for the above, 48 is the number of frames that the GIF was composed of, and it assumes the frames are in the project directory in a subfolder called gifs.

"},{"location":"library/snippets/#line-of-travel","title":"Line of travel","text":"

In this example we use a geometry generator to create a line between the origin point and the destination point:

if (@from_feature_id = $id OR @to_feature_id = $id,\n -- read this from inside to out so \n -- last tranform the geometry back to the map crs\n transform( \n  -- densify the geometry so that when we transform\n  -- back it makes a great circle\n  densify_by_count(  \n   -- move the geometry into a crs that \n   -- shows a great circle as a straight line\n   transform( \n    -- make a line from the previous pont to the next point\n    make_line( \n     geometry(@from_feature), \n     geometry(@to_feature)\n    ),  \n    @map_crs, 'EPSG:4326'),\n   99), \n  'EPSG:4326',  @map_crs),\n None)\n

"},{"location":"library/snippets/#showing-diagnostic-info-as-a-copyright-label","title":"Showing diagnostic info as a copyright label","text":"

Showing diagnostic information in the QGIS copyright label:

[%\n'Feature Variables:' ||\n' \\n------------------------' ||\n' \\nPrevious Feature ' || to_string(coalesce(attribute(@previous_feature, 'name'), '-'))  ||\n' \\nPrevious Feature ID ' || to_string(coalesce(@previous_feature_id, '-'))  ||\n' \\n' ||\n' \\nNext Feature ' || to_string(coalesce(attribute(@next_feature, 'name'), '-'))  ||\n' \\nNext Feature ID ' || to_string(coalesce(@next_feature_id, '-'))  ||\n' \\n' ||\n' \\nHover Feature ' || to_string(coalesce(attribute(@hover_feature, 'name'), '-'))  ||\n' \\nHover Feature ID ' || to_string(coalesce(@hover_feature_id, '-'))  ||\n' \\n' ||\n' \\nFrom Feature ' || to_string(coalesce(attribute(@from_feature, 'name'), '-'))  ||\n' \\nFrom Feature ID ' || to_string(coalesce(@from_feature_id, '-'))  ||\n' \\n' ||\n' \\nTo Feature ' || to_string(coalesce(attribute(@to_feature, 'name'), '-'))  ||\n' \\nTo Feature ID ' || to_string(coalesce(@to_feature_id, '-'))  ||\n' \\n' ||\n' \\nTotal Hover Frames ' || to_string(coalesce(@hover_frames, 0))  ||\n' \\nCurrent Hover Frame ' || to_string(coalesce(@current_hover_frame, 0))  ||\n' \\nTotal Travel Frames ' || to_string(coalesce(@travel_frames, 0))  ||\n' \\nCurrent Travel Frame ' || to_string(coalesce(@current_travel_frame, 0))  ||\n' \\nTotal Frame Count ' || to_string(coalesce(@total_frame_count, 0))  ||\n' \\nFrame Number ' || to_string(coalesce(@frame_number, 0))  ||\n' \\nFrame Rate ' || to_string(coalesce(@frame_rate, 0))  ||\n' \\nwith Current Animation Action: ' || @current_animation_action ||\n' \\nTo Direction ' ||  coalesce(format_number(degrees(azimuth( geometry(@hover_feature), geometry(@previous_feature) ) ) ), 0) || \n' \\nFrom Direction ' ||  coalesce(format_number(degrees( azimuth( geometry(@hover_feature), geometry(@next_feature) ) ) ), 0)\n%]\n\n\n

Example output:

"},{"location":"library/snippets/#variable-size-of-labels","title":"Variable size of labels","text":"

Variably changing the size on a label as we approach it in the animation:

```40 * ((@frame_number % @hover_frames) / @hover_frames)

\n## Calculating the angle between points\n\nYou can calculate the angle between the hover point and the previous point like this:\n\n```python\ncoalesce(\n format_number(\n  degrees( \n   azimuth( \n    geometry(@hover_feature), \n    geometry(@previous_feature) \n   )\n  )\n ), 0)\n
"},{"location":"library/snippets/#rotation","title":"Rotation","text":"

You can set the angle of rotation for a symbol using this expression:

Using this technique you can also create an animation effect showing the source direction of travel and the new destination.

scale_linear (\n @current_hover_frame,\n 0,\n @hover_frames,\n degrees( \n  azimuth( \n   geometry(@hover_feature), \n   geometry(@previous_feature) \n  )\n ),\n degrees( \n  azimuth( \n   geometry(@hover_feature), \n   geometry(@next_feature) \n  )\n )\n)\n

Will produce something like this:

"},{"location":"library/snippets/#flying-points-cluster","title":"Flying points cluster","text":"

Here is an example where we animate all the points in a cluster that are not the hover point. We use an easing function to make the animation have an interesting circular motion.

-- Taken from https://spicyyoghurt.com/tools/easing-functions\n--    t = Time - Amount of time that has passed since the beginning of the animation. Usually starts at 0 and is slowly increased using a game loop or other update function.\n--    b = Beginning value - The starting point of the animation. Usually it's a static value, you can start at 0 for example.\n--    c = Change in value - The amount of change needed to go from starting point to end point. It's also usually a static value.\n--    d = Duration - Amount of time the animation will take. Usually a static value aswell.\n-- Sinusoidal\n-- -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;\n\n-- Use with the animation in static mode\nif(@hover_feature_id != $id,\narray(\n  (-@hover_frames / 2) * (cos( (pi() * @frame_number / @hover_frames ) - 1)) ,\n (-@hover_frames / 2) * (sin( (pi() * @frame_number / @hover_frames ) - 1)) \n),\narray (0,0))\n\n

This function should be applied to the offset X,Y property of the symbol.

"},{"location":"library/variables/","title":"\ud83e\uddee QGIS Expression Variables","text":"

The animation workbench exposes or modifies a number of different QGIS Expression variables that you can use to achieve different dynamic rendering effects.

"},{"location":"library/variables/#common-variables","title":"Common variables","text":"

These variables will always be available, regardless of the animation mode

Variable Notes frame_number Frame number within the current dwell or pan range. frame_rate Number of frames per second that the video will be rendered at. total_frame_count Total number of frames for the whole animation across all features."},{"location":"library/variables/#fixed-extent-mode-variables-with-layer","title":"Fixed extent mode variables (with layer)","text":"

These variables are available when in the fixed extent animation mode when a vector layer has been set

Variable Notes hover_feature The feature we are currently hovering over hover_feature_id Feature ID for the feature we a current hovering over previous_feature The previously visited feature (or NULL if there isn't one) previous_feature_id Feature ID for the previously visited feature (or NULL if there isn't one) next_feature The next feature to visit after the current one (or NULL if there isn't one) next_feature_id Feature ID for the next feature to visit after the current one (or NULL if there isn't one) current_hover_frame The frame number for the current feature (i.e. how many frames we have hovered at the current feature) hover_frames Number of frames we will hover at the current feature for current_animation_action Always \"Hovering\""},{"location":"library/variables/#planarsphere-modes","title":"Planar/Sphere modes","text":"

These variables are available in the Planar or Sphere mode.

Variable Notes current_animation_action Either \"Hovering\" or \"Travelling\""},{"location":"library/variables/#when-hovering","title":"When hovering","text":"

These variables are available in planar or sphere mode, when the animation is currently hovering over a feature

Variable Notes hover_feature The feature we are currently hovering over hover_feature_id The feature ID for the feature we are currently hovering over previous_feature The previously visited feature (or NULL if there isn't one) previous_feature_id Feature ID for the previously visited feature (or NULL if there isn't one) next_feature The next feature to visit after the current one (or NULL if there isn't one) next_feature_id Feature ID for the next feature to visit after the current one (or NULL if there isn't one) current_hover_frame The frame number for the current feature (i.e. how many frames we have hovered at the current feature) hover_frames Number of frames we will hover at the current feature for"},{"location":"library/variables/#when-travelling","title":"When travelling","text":"

These variables are available in planar or sphere mode, when the animation is currently travelling between two features

Variable Notes from_feature The feature we are travelling away from from_feature_id The feature ID for the feature we are travelling away from to_feature The feature we are heading toward to_feature_id The feature ID for the feature we are heading toward current_travel_frame The frame number for the current travel operation travel_frames Number of frames we will travel between the current features"},{"location":"library/variables/#example-expressions","title":"Example expressions","text":"

Visit the snippets section of our documentation for example expressions.

"},{"location":"manual/project_preparation/","title":"How to set up a project to work with the animation plugin","text":"

1. The first step for getting an output using the Workbench is to create a QGIS Project \u00a0 Open QGIS and click on Project \u2794 New

Next, add new layers to your project. You will want a few layers; one, or more, backing layer(s) (vector layers or XYZ Tiles), a layer for the workbench to follow, and one, or more, layer(s) of animated points. The example in this section only has one animated layer. \u00a0

To add a layer, go to Layer \u2794 Create Layer and then select the type of layer you want to add. The example adds a point layer to a GeoPackage to make the project more portable.

Once you have added your layers you need to add features to the layers. This is done by selecting a layer and then clicking Toggle Editing (1) \u2794 Add PointFeature (2). Then click around on your map to add as few, or as many, features as you need.

The example project has four layers: two point layers (3) and two backing layers (4).

A simple way to add a vector base layer is to type \"world\" into the coordinate textbox

Finally, style your layers to make your project look aesthetically pleasing. To style your layers you must select the layer you want to style and then using the Layer Styling toolbar, play around with the style of the layer until it suits you. A good practice is to have your backing layers as more muted colours and your desired features as more eye-catching colours.

You now have a QGIS Project. \u00a0

2. The next step is to choose which features you want to be animated. \u00a0

Pick the layer (or layers) that you want to have animations. Then either find, or create, the animation for the layer. Make sure you have all the correct attribution for any animations you use. Below is an example of a simple fish animation split into its frames. The frames are repeated to slow down the animation's playback speed.

  • Now use the QGIS Expressions system with the variables introduced by the Animation Workbench to define behaviours of your symbols during flight and hover modes of your animation. \u00a0 Select the layer you want to animate and open the Layer Styling toolbar.

If you are using QGIS 3.26 you can simply use the new animated point symbol, or if you're using an older version of QGIS 3.x follow the instructions below.

The layer should contain a Raster Image Marker. Once you have selected the marker you want to use click on the QGIS Expressions dropdown menu (5) and click on Edit (6).

You can also make a marker move along a line relative to the frame of the animation. Use the Code Snippets Section for more in-depth help.

The example below works with the animation from earlier.

@project_home\n|| \n'/fish/fish_00'\n||\nlpad(to_string( @frame_number % 32), 2, '0')\n||\n'.png'\n

3. Configure your animation

After animating your markers it's time to configure your animation. Open the Animated Workbench and begin choosing between the different modes and options. \u00a0

Open the Workbench by clicking the Animation Workbench (7) icon in the Plugin Toolbar.

Configure the settings for your animation. The screenshot below is configured for the example presented in this section. The Animation Layer is selected as route (8) because that is the path that the output animation will fly along. The Zoom Range (9) was selected from the Map Canvas Extent, and the Frame rate per second (fps) (10) was set to match the number of frames of the animated markers so that they will play nicely in the output. The other settings were selected as a personal choice.

Select the Output Resolution (11) and a location for your output by clicking on the ellipsis (three dots) or by typing in the desired file path (12).

Refer to the Workbench User Interface section for more information about what various settings and buttons accomplish.

4. Render your animation! \u00a0 Click Run and render your output. The output below is the output from the example.

"},{"location":"manual/under_the_hood/","title":"What is the Workbench doing?","text":"
  • What does the workbench do?

    The workbench creates animations from QGIS by generating multiple static frames (images) and then combining those frames into an animation. The user tells QGIS how the frames should change from one to the other. In QGIS 3.26 and later the animated markers allow markers to be animated without the use of the expressions system. \u00a0

  • How do the animated markers work?

    In the code snippet below, the user tells QGIS that as the frame count increments by one the Raster Image Marker should change to the next image in the sequence.

    py @project_home || '/fish/fish_00' || lpad(to_string( @frame_number % 32), 2, '0') || '.png'

    The user specifies the path of the image (@project_home/fish/fish_00). Then the lpad(to_string( @frame_number % 32), 2, '0') tells QGIS to convert the frame number to a string and then modulus the number of frames by the number of animation frames (32) (i.e. QGIS divides the number of frames by 32 and then repeats the sequence when the remainder is zero). The 2 and '0' in the snippet tell QGIS to pad the /fish/fish_00 with two zeroes at the end. Finally the '.png' tells QGIS the type of file to finish off the path. \u00a0

  • Frame Output location on Windows

    For users on a Windows machine who are interested in seeing the frames before they are combined into an animation (GIF or movie) you can find them by going to \"C:\\Users\\Username\\AppData\\Local\\Temp\\animation_workbench-0000000000.png\". Bear in mind that AppData is a hidden file, so it's preferable to not make changes unless explicitly told otherwise. \u00a0

  • Frame Output on Linux

    The frames should be in your /tmp directory.

"},{"location":"manual/workbench_ui/","title":"The Workbench User Interface","text":""},{"location":"manual/workbench_ui/#animation-plan","title":"Animation Plan","text":"
  • Render Modes (1): These determine the behaviour and type of animation
  • Sphere: The coordinate reference system (CRS) will be manipulated to create a spinning globe effect. Like Google Earth might do, but with your own data and cartography.
  • Planar: The coordinate reference system (CRS) will not be altered, but the camera will pan and zoom to each point. It lets you move from feature to feature on a flat map, pausing at each if you want to.
  • Fixed extent: The frame of reference stays the same and you can animate the symbology within that scene.

  • Animation Layer (2):

  • Dropdown menu: This allows you to select which map layer you want the animation to follow.
  • Loop from final feature back to first feature: allows for a seamlessly looping output GIF or movie(MP4).

  • Zoom Range (3): The scale range that the animation should move through.

  • Minimum (exclusive): The zenith (highest point) of the animation when it zooms out while travelling between points, i.e. the most \"zoomed out\".
  • Maximum (inclusive): The scale (zoom level) used when we arrive at each point, i.e. the most \"zoomed in\".

  • Data defined settings (4)

  • Scale

    • Minimum: User-defined minimum scale
    • Maximum: User-defined maximum scale
  • Animation Frames (5)

  • Frame rate per second (fps): When writing to video or gif, how many frames per second to use.
  • Travel Duration: This is the number of seconds that the animation will take during animation from one feature to the next.
  • Feature Hover duration: This is the number of seconds that the animation will hover over each feature.

  • Extent (6):
  • Can be manually entered using North, East, South, and West coordinates as limits.
  • Can be calculated from a map layer, the layout map, or a bookmark.
  • Can be set to match the Map Canvas Extent
  • Can be set as a rectangular extent using the Draw on Canvas feature.

  • Pan and Zoom Easings (7)

  • What are Easings: Easings are transitions from one state to another along a smooth curve. A user can specify the shape of the curve used.
  • Pan Easings (XY): The pan easing will determine the motion characteristics of the camera on the X and Y axis as it flies across the scene (i.e. how it accelerates or decelerates between points)
  • Zoom Easing (Z): The pan easing will determine the motion characteristics of the camera on the Z axis as it flies across the scene (i.e. how the camera zooms in and out of the points)

  • Frame previews (8): A preview of what each frame of the animation will look like. A user can decide which Frame to view.

"},{"location":"manual/workbench_ui/#intro-tab","title":"Intro Tab","text":"

Edit the intro section of the generated movie here.

  • Media: List of the various images or movies selected for the intro section. You can drag and drop items in the list to change the play order.
  • Add Media (Plus sign) (1): Add images or movies
  • Remove Media (Minus sign) (2): Remove images or movies

  • Duration (3): For images, you can set a duration for each image (in seconds).

  • Preview Frame (4): This shows what the media will look like.

  • Details: Provides details about where the media is stored on your computer.

"},{"location":"manual/workbench_ui/#outro-tab","title":"Outro Tab","text":"

Edit the outro section of the generated movie here.

  • Media: List of the various images or movies selected for the outro section. You can drag and drop items in the list to change the play order.
  • Add Media (Plus sign) (1): Add images or movies
  • Remove Media (Minus sign) (2): Remove images or movies

  • Duration (3): For images, you can set a duration for each image (in seconds).

  • Preview Frame (4): This shows what the media will look like.

  • Details: Provides details about where the media is stored on your computer.

"},{"location":"manual/workbench_ui/#soundtrack-tab","title":"Soundtrack Tab","text":"
  • Media: List of the various sound files (.mp3 or .wav) to play during the generated movie. You can drag and drop items in the list to change the play order.
  • Add Media (Plus sign) (1): Add sound files (.mp3 or .wav) to play during the generated movie.
  • Remove Media (Minus sign) (2): Remove sound files (.mp3 or .wav)

  • Duration (3): The cumulative length of your soundtracks should be as long, or longer, than your movie, including the intro/outro sections. If the soundtrack is longer than the movie it will be truncated (shortened) when the movie ends.

  • Details: Provides details about where the media is stored on your computer.

"},{"location":"manual/workbench_ui/#output","title":"Output","text":"
  • Output Options: Select which output format you would like. Regardless of the format chosen, a folder of images will be created, one image per frame.
  • Re-use cached Images (1): This will not erase cached images on disk and will resume processing from the last cached image.
  • Animated GIF (2): For this export to work, you need to have the ImageMagick 'convert' application available on your system.
  • Movie (MP4) (3): For this option to work, you need to have the 'ffmpeg' application on your system.
  • Output Resolution (4): Allows a user to specify one of four image resolutions for the output animation. The numbers in brackets for the first three options represent the width and height of the output in pixels (i.e. width x height), and the fourth option matches the output's size to the size of the Map Canvas on the screen.
  • File selection (ellipsis) (5): This lets a user select the location where the output will be stored.
"},{"location":"manual/workbench_ui/#progress","title":"Progress","text":"
  • Frame Preview (1): A preview of what each frame of the animation will look like. It changes automatically as the workbench runs.
  • Progress (2): This provides a detailed look at what is happening while the workbench runs.
  • Total Tasks: This number represents the total number of frames that will be generated by the workbench.
  • Completed Tasks: The number of tasks that have completed being processed.
  • Remaining Features: The number of features from your animation layer that still need to be processed.
  • Active Tasks: The number of tasks (threads) currently being run by the workbench
  • Features Complete: The number of tasks that have been processed by the workbench.
  • Logs (3): A detailed list of what steps the workbench is doing (a record of processing)
  • Progress Bar (4): A visual representation of the workbench's progression as a percentage.
"},{"location":"manual/workbench_ui/#other-buttons","title":"Other Buttons","text":"
  • Run: Starts the process of getting an output from the workbench. It is greyed out until a user provides a destination for the output file.
  • Close: Closes the workbench.
  • Cancel: Ends the workbench processing at whatever point it has reached when the button is pressed.
  • Help: Opens a link to the Animation Workbench documentation.
"},{"location":"start/configure/","title":"Initial Configuration","text":"

There is nothing really to configure! We do provide a few options in the configuration dialog, but most users should not need to change them.

You can access the QGIS Animation Workbench plugin options by opening the standard QGIS Setting dialog and clicking on the animation workbench tab.

Settings \u2794 Options

  • Animation Workbench plugin Options (1)

Currently there are just three configuration options:

  • Number of concurrent render tasks (2): This is the number of concurrent tasks that will be used to render animations. The default is 1.
  • Enable developer mode (3): This is a developer option that enables the developers to see an icon in the toolbar which will start the debug remote server.
  • Verbose logging mode (4): This will add extra messages in the logging pane to help you understand what is going on during the rendering process.
"},{"location":"start/install/","title":"Installing the QGIS Animation Workbench plugin","text":"

\u26a0\ufe0f Please take note: We have not yet published the plugin in the QGIS Plugin Repository, but when we do you will be able to access it simply by clicking on the \"QGIS Animation Workbench\" option in the QGIS Plugin Manager.

"},{"location":"start/install/#manual-install-from-github-tagged-release","title":"Manual install from GitHub (tagged release)","text":"

To install, visit the Github Repository, click on the Actions tab, and click on the Make QGIS Plugin Zip For Manual Installs workflow (the bottom one).

Click on the most recent workflow run (the top one).

Scroll down on the on the page.

And click on animation_workbench to download the most recent build of the plugin

Download the animation_workbench.zip file and open it in QGIS using the plugin manager as described below.

  1. Open QGIS
  2. Plugins \u27a1 Manage and install plugins ...
  3. Choose the Install from zip tab
  4. Select the animation_workbench.zip download
  5. Click the Install Plugin button.
"},{"location":"start/install/#install-from-plugin-manager","title":"Install from plugin manager","text":"

\u26a0\ufe0f Please take note: We have not yet published the plugin in the QGIS Plugin Repository, in the mean time please follow the steps above to do the installation.

To access the QGIS Plugin Manager you simply need to select Plugins \u27a1 Manage and Install Plugins... (1) in the Menu Toolbar.

Once the QGIS Plugin Manager loads, you need to navigate to the All (2) tab and type \"animation\" into the search bar (3). Select QGIS Animation Workbench from the list of available plugins and then select Install Plugin (4).

Once the Animation Workbench is installed, you can access it by clicking on the Animation Workbench icon (5) in the Plugin Toolbar.

Note if you are on Ubuntu, you may need to install the Qt5 multimedia libraries.

sudo apt install PyQt5.QtMultimedia\n
"},{"location":"start/using/","title":"Using the Animation Workbench","text":"

In this section, we describe the general workflow for using the Animation Workbench.

"},{"location":"start/using/#process-overview","title":"Process Overview","text":"
  1. Create a QGIS project!
  2. Identify features that will be animated.
  3. Use the QGIS Expressions system with the variables introduced by the Animation Workbench to define behaviours of your symbols during flight and hover modes of your animation.
  4. Open the Animation Workbench and configure your animation, choosing between the different modes and options.
  5. Render your animation!
"},{"location":"start/using/#more-in-depth-process","title":"More in Depth Process","text":"

1. Create a QGIS Project \u00a0 Open QGIS and click on Project \u2794 New

Add new layers to your project

A simple way to add a base layer is to type \"world\" (1) into the coordinate textbox

Style the layers you've added to make your project look a bit better. Select the layer (2) you want to style and in the Layer Styling toolbar (3), style the layer to look appealing to you.

2. Identify features that will be animated. \u00a0

Pick the layer (or layers) that you want to animate. Then either find or create the animation for the layer. Make sure you have all the correct attribution for any animations you use. Below is an example of an animation split into its frames.

3. Use the QGIS Expressions system with the variables introduced by the Animation Workbench to define behaviours of your symbols during flight and hover modes of your animation. \u00a0

Select the layer you want to animate and open the Layer Styling toolbar.

If you are using QGIS 3.26 you can simply use the new animated point symbol, or if you're using an older version of QGIS 3.x follow the instructions below.

The layer should be a Raster Image Marker. Once you have selected the image you want to use click on the QGIS Expressions dropdown menu (4) and click on Edit (5).

Use the Code Snippets Section for more in depth help. The example below works with the bird animation from earlier

   @project_home\n   ||\n   '/bird/bird_00'\n   || \n   lpad(to_string(@frame_number % 9), 2, '0')\n   || \n   '.png'\n

1. Open the Animation Workbench and configure your animation, choosing between the different modes and options. \u00a0

Open the Workbench by clicking the Animation Workbench (6) icon in the Plugin Toolbar.

Configure the settings for your animation. The screenshot below is configured for the example presented in this section. The Animation Layer is selected as route (7) because that is the path the animation will fly along, the Zoom Range (8) was selected from the Map Canvas Extent, and the Frame rate per second (9) was set to 9 to match the bird animation.

Set your desired Output Options (10) Select a location for your output (11).

Refer to the Workbench User Interface Section for more information about what various settings and buttons accomplish.

2. Render your animation! \u00a0 Click Run and render your output. The output below is the output from the example.

"},{"location":"tutorials/","title":"QGIS","text":""},{"location":"tutorials/#tutorial-1-point-along-a-line","title":"Tutorial 1: Point Along A Line","text":"

This tutorial introduces the concept of moving a point along a line within your animated map.

1. Download and extract the Required Tutorial Zip Folder

2. Open the tutorial_1.qgz project file that is in the folder. When you first open it you you see something like this:

3. Select the premade line layer (1), and click on the Add Symbol Layer (green plus symbol) button (2) to it.

Change the new Symbol Layer (3) type to marker line and then style it (4) so that it is more visible.

4. Change the Symbol Layer's settings so that the point is only on the first vertex (5) and and not at equidistant intervals.

Change the offset along the line to be Percentage (6).

Click the Dropdown Menu (7) \u2794Edit... (8) and then add the following code snippet

    -- Point Along Line Code Snippet\n    (@current_hover_frame/@hover_frames) * 100\n

The snippet tells QGIS how far along the line (as a percentage of the line length) to render the point in each frame.

5. Open the Workbench and select Fixed Extent (9).

Click on Map Canvas Extent (10) and set the the Frames to 300 (11) (for a 10 second output at 30 frames per second).

6. Skip over the Intro, Outro, and Soundtrack tabs. In the Output tab, set the output format (12) and resolution (13), and set the output location's path (14).

7. Click Run and render your output.

After this tutorial you should have a better idea of how to make a point move along a line. An expansion to this example would be to make the moving point a dynamically changing marker (like the markers in tutorial 1). Go have fun!

"},{"location":"tutorials/#tutorial-2-basic-dynamically-changing-markers","title":"Tutorial 2: Basic Dynamically Changing Markers","text":"

This tutorial aims to show you the basics of creating, and animating, a static layer to use with the Animation Workbench. There are three pre-made layers to allow the main focus of the tutorial to be on the Animation Workbench and not on QGIS as a whole.

1. Download and extract the Required Tutorial Zip Folder

2. Open the tutorial_2.qgz project file that is in the folder. \u00a0

3. Set the CRS of your project to WGS84/UTM zone 35S (EPSG: 32735).

4. In the Browser, expand the tutorial_2.gpkg and add the three pre-made layers (VaalDam, SouthAfrica, and route) (A) to your project.

5. In the Layers Panel, arrange the layers in the following order: route, VaalDam, SouthAfrica. Then right-click on the VaalDam layer and Zoom to Layer(s) (B)

Style the three layers to your preferred style. \u00a0

6. Now create a new layer in the tutorial_2.gpkg by clicking Layer\u2794Create Layer\u2794 New GeoPackage Layer... (C).

Click on the Ellipsis (D), navigate to and select the tutorial_2.gpkg, and click Save. Change the Table name to fish (E), set the Geometry type as Point (F), and change the CRS to match the Project CRS (G).

Click on OK and then click Add New Layer on the window that pops up. \u00a0

7. Select the fish layer and then click on Toggle Editing\u2794Add Point Feature (H).

Add a few points wherever you feel they should go (Hint: This is a fish layer so adding them above the dam layer would be best). Don't worry about naming the points, just add them.

Save your changes by clicking on Save Layer Edits just next to the Toggle Editing button. Then stop editing the layer. \u00a0

8. Repeat steps 6. and 7. but change the Table name to bird and add the points over the land areas.

9. Select the fish layer and then in the Layer styling toolbar (I) change the Symbol layer type to Raster Image Marker (J).

Select the marker image by clicking the Dropdown menu\u2794Select File... (K) and then choosing fish\u2794fish_0000.png.

Click Open

10. Change the marker's Size Unit to Meters at Scale (L)

and set the Width and Height to 1000. \u00a0

11. Repeat Steps 9. and 10. with the bird layer but instead choosing bird\u2794bird_0000.png and setting the Width and Height to 3000.

In QGIS 3.26, or later, the Symbol layer type can simply be selected as Animated Marker and Step 12. can be skipped.

12. To animate the fish and bird layers using the QGIS Expressions system click the Dropdown Menu\u2794Edit... (M).

For the fish layer use the following expression:

    @project_home\n    || \n    '/fish/fish_00'\n    ||\n    lpad(to_string( @frame_number % 32), 2, '0')\n    ||\n    '.png'\n

And for the bird layer use:

    @project_home\n    ||\n    '/bird/bird_00'\n    || \n    lpad(to_string(@frame_number % 9), 2, '0')\n    || \n    '.png'\n

Refer to the What is the Workbench doing? section for an explanation about what the above code snippet is doing.

13. Open the Animation Workbench (refer to the Using the Animation Workbench section if you are unsure how to open the Workbench).

In the Animation Plan tab set:

  • the Render Mode to Planar (N),
  • the Animation Layer to route (O) using the dropdown menu,
  • the Zoom Range (P) to 1:270000 for the Minimum and 1:135000 for the Maximum,
  • the Frame rate per second to 9 fps (Q),
  • the Travel duration to 4,00 s (R),
  • and the Feature hover duration to 2,00 s (S)

Enable both the Pan and Zoom easings and set them to linear.

14. Skip past the Intro, Outro, and Soundtrack tabs to the Output tab. Set the Output Format as Animated Gif (T) and the Output Resolution to 720p (1280x720) (U). The Output Resolution can be set as any of the three choices but was set at 720 for this tutorial for the sake of speed. Set the output location to one you can find easily (V)

15. Click Run and watch what the Workbench is doing in the Progress tab. Once the Workbench is finished running, you should end up with an output similar to this:

After this tutorial you should have a better understanding of how to create a point layer in your project and then to change the Single Symbol markers into stationary animated markers. A key focus is the idea that you can tell versions of QGIS before 3.26 to dynamically change markers using short code snippets. Versions of QGIS post 3.26 allow a user to simply use the Animated Marker feature without editing an expression.

"},{"location":"tutorials/#tutorial-3-flying-points","title":"Tutorial 3: Flying Points","text":"

This tutorial aims to show you how add a flying point animation to points on your map using built-in QGIS functionalities (The geometry generator line) and introduced variables from the workbench.

1. Download and extract the Required Tutorial Zip Folder

2. Open the tutorial_3.qgz project file. When you first open the project file you should be greeted with something like this:

3. Create a new point layer in a new geopackage by clicking Layer\u2794Create Layer\u2794 New GeoPackage Layer.... Click on the Ellipsis (three dots) next to the Database textbox and navigate to the folder that the tutorial_3.qgz file is located in. Type the File name \"tutorial_3\" (1) and ensure the file will be saved as a GeoPackage (2) and click Save (3).

Change the Table name to flying_points (4), set the Geometry type as Point (5) and change the CRS to match the Project CRS (6).

Click OK (7) \u00a0

4. Click on Toggle Editing\u2794Add Point Feature (8).

And randomly add points to your map. Depending on your computer's capabilites, you can add more, or fewer, points than the example below.

Save your Layer Edits and toggle off the Editing tool. \u00a0

5. Style the points layer.

Select the flying_points (9) layer and in the Layer Styling toolbar click on the Add Symbol Layer (green plus symbol) button (10).

Select the top Simple Marker (11) and change its Symbol layer type to Geometry Generator (12)

and then set the Geometry type to LineString / MultiLineString (13).

Change the line's Symbol layer type to Marker Line (14).

Add a second Simple marker to the marker line so that you end up with something like this:

Style the various Simple Markers to your preferred look.

6. Select the Geometry Generator symbol layer (15) and add this code to it:

    wave_randomized(\n    make_line(\n    $geometry, geometry(@hover_feature)), \n    100, 1000, 1000, 10000, 1)\n

More information about what changing the numbers will affect can be found in the QGIS expressions editor. \u00a0

7. A few options need to be changed in the Marker Line symbol layer (16): The Marker placement needs to be set to On first vertex (17) and, the Offset along line needs to be changed to Percentage (18). The click the Dropdown menu next to Offset along line and select Edit... (19).

In the Expression String Builder add the following code snippet:

    100 - to_int((@current_hover_frame / @hover_frames) * 100 )\n

Click OK

8. Select the first Simple Marker symbol layer (20) in the Marker Line symbol layer. Scroll down to Offset and click on the Dropdown Menu\u2794Edit.. (21).

In the Expression String Builder add the following code snippet:

    -- Taken from https://spicyyoghurt.com/tools/easing-functions\n    --    t = Time - Amount of time that has passed since the beginning of the animation. Usually starts at 0 and is slowly increased using a game loop or other update function.\n    --    b = Beginning value - The starting point of the animation. Usually it's a static value, you can start at 0 for example.\n    --    c = Change in value - The amount of change needed to go from starting point to end point. It's also usually a static value.\n    --    d = Duration - Amount of time the animation will take. Usually a static value aswell.\n    -- Sinusoidal\n    -- -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;\n\n    -- Use with the animation in static mode\n    if(@hover_feature_id != $id,\n    array(\n        (-@hover_frames / 2) * (cos( (pi() * @frame_number / @hover_frames ) - 1)) ,\n        (-@hover_frames / 2) * (sin( (pi() * @frame_number / @hover_frames ) - 1)) \n        ),\n        array (0,0))\n

Click OK

9. Open the Animation Workbench (22)

10. Set up the Animation Plan with:

  • the Render Mode to Planar (23),
  • the Animation Layer to flying_points (24) using the dropdown menu,
  • the Zoom Range (25) to 1:22000000 for the Minimum and 1:11000000 for the Maximum,
  • the Frame rate per second to 9 fps (26),
  • the Travel duration to 2,00 s (27),
  • the Feature hover duration to 2,00 s (28),
  • and the Zoom Easing as InCirc (29)

With a decently specced computer you can up the fps and get the points to fly faster in your output. \u00a0

11. Add license-free media to the Intro, Outro, and Soundtrack.

Make sure your Soundtrack is as long as, or longer than, your final animation will be (including the Intro, Animation, and Outro).

12. Set the Output Format as Movie (MP4) (30) and the Output Resolution to 1080 (1920x1080) (31). The Output Resolution can be set as any of the three choices but was set at 1080 for this tutorial for the sake of speed. Set the output location (32) to one you can easily locate.

13. Click Run and get an output. The GIF below is the visual output of the tutorial if you followed step-by-step and set the parameters to exactly what was stated.

The link to a more complex output (with an Intro, an Outro, and a Soundtrack) can be found here

After this tutorial you should have a better idea of how you can use a mixture of built-in QGIS functionalites and the workbench's introduced variables to generate interesting outputs.

"}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..e2affed --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,83 @@ + + + + None + 2023-10-07 + daily + + + None + 2023-10-07 + daily + + + None + 2023-10-07 + daily + + + None + 2023-10-07 + daily + + + None + 2023-10-07 + daily + + + None + 2023-10-07 + daily + + + None + 2023-10-07 + daily + + + None + 2023-10-07 + daily + + + None + 2023-10-07 + daily + + + None + 2023-10-07 + daily + + + None + 2023-10-07 + daily + + + None + 2023-10-07 + daily + + + None + 2023-10-07 + daily + + + None + 2023-10-07 + daily + + + None + 2023-10-07 + daily + + + None + 2023-10-07 + daily + + \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz new file mode 100644 index 0000000..6d4df27 Binary files /dev/null and b/sitemap.xml.gz differ diff --git a/start/configure/index.html b/start/configure/index.html new file mode 100644 index 0000000..9e357f1 --- /dev/null +++ b/start/configure/index.html @@ -0,0 +1,1029 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Initial Configuration - QGIS Animation Workbench + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Initial Configuration

+

There is nothing really to configure! We do provide a few options in the +configuration dialog, but most users should not need to change them.

+

You can access the QGIS Animation Workbench plugin options by opening the standard +QGIS Setting dialog and clicking on the animation workbench tab.

+
+

Settings ➔ Options

+
+

Settings Dialog

+
    +
  • Animation Workbench plugin Options (1)
  • +
+

Currently there are just three configuration options:

+
    +
  • Number of concurrent render tasks (2): This is the number of concurrent tasks +that will be used to render animations. The default is 1.
  • +
  • Enable developer mode (3): This is a developer option that enables the developers +to see an icon in the toolbar which will start the debug remote server.
  • +
  • Verbose logging mode (4): This will add extra messages in the logging pane to +help you understand what is going on during the rendering process.
  • +
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/start/img/001_PluginManager_1.png b/start/img/001_PluginManager_1.png new file mode 100644 index 0000000..dbddb19 Binary files /dev/null and b/start/img/001_PluginManager_1.png differ diff --git a/start/img/002_SearchForPlugin_1.png b/start/img/002_SearchForPlugin_1.png new file mode 100644 index 0000000..fdfe573 Binary files /dev/null and b/start/img/002_SearchForPlugin_1.png differ diff --git a/start/img/003_AWLaunch_1.png b/start/img/003_AWLaunch_1.png new file mode 100644 index 0000000..5fb1680 Binary files /dev/null and b/start/img/003_AWLaunch_1.png differ diff --git a/start/img/004_NewProject_1.png b/start/img/004_NewProject_1.png new file mode 100644 index 0000000..47595d3 Binary files /dev/null and b/start/img/004_NewProject_1.png differ diff --git a/start/img/005_AddLayers_1.png b/start/img/005_AddLayers_1.png new file mode 100644 index 0000000..c4e140a Binary files /dev/null and b/start/img/005_AddLayers_1.png differ diff --git a/start/img/006_StylingLayers_1.png b/start/img/006_StylingLayers_1.png new file mode 100644 index 0000000..3a71fac Binary files /dev/null and b/start/img/006_StylingLayers_1.png differ diff --git a/start/img/007_AnimatedLayer_1.png b/start/img/007_AnimatedLayer_1.png new file mode 100644 index 0000000..e812ba6 Binary files /dev/null and b/start/img/007_AnimatedLayer_1.png differ diff --git a/start/img/008_EditExpression_1.png b/start/img/008_EditExpression_1.png new file mode 100644 index 0000000..008f6e6 Binary files /dev/null and b/start/img/008_EditExpression_1.png differ diff --git a/start/img/009_Expression_1.png b/start/img/009_Expression_1.png new file mode 100644 index 0000000..586268a Binary files /dev/null and b/start/img/009_Expression_1.png differ diff --git a/start/img/010_OpenAW_1.png b/start/img/010_OpenAW_1.png new file mode 100644 index 0000000..ecc7403 Binary files /dev/null and b/start/img/010_OpenAW_1.png differ diff --git a/start/img/011_OutputSetup_1.png b/start/img/011_OutputSetup_1.png new file mode 100644 index 0000000..268d3c8 Binary files /dev/null and b/start/img/011_OutputSetup_1.png differ diff --git a/start/img/012_Output_1.png b/start/img/012_Output_1.png new file mode 100644 index 0000000..6046617 Binary files /dev/null and b/start/img/012_Output_1.png differ diff --git a/start/img/013_Configure_1.png b/start/img/013_Configure_1.png new file mode 100644 index 0000000..e204c5d Binary files /dev/null and b/start/img/013_Configure_1.png differ diff --git a/start/img/QAW-IntroThumbnail.jpg b/start/img/QAW-IntroThumbnail.jpg new file mode 100644 index 0000000..aa6ecf5 Binary files /dev/null and b/start/img/QAW-IntroThumbnail.jpg differ diff --git a/start/img/install_0000.png b/start/img/install_0000.png new file mode 100644 index 0000000..43cd45e Binary files /dev/null and b/start/img/install_0000.png differ diff --git a/start/img/install_0001.png b/start/img/install_0001.png new file mode 100644 index 0000000..c8e2f8e Binary files /dev/null and b/start/img/install_0001.png differ diff --git a/start/img/install_0002.png b/start/img/install_0002.png new file mode 100644 index 0000000..4d38398 Binary files /dev/null and b/start/img/install_0002.png differ diff --git a/start/img/install_0003.png b/start/img/install_0003.png new file mode 100644 index 0000000..7cdec8b Binary files /dev/null and b/start/img/install_0003.png differ diff --git a/start/img/options.png b/start/img/options.png new file mode 100644 index 0000000..d199aa7 Binary files /dev/null and b/start/img/options.png differ diff --git a/start/img/output.gif b/start/img/output.gif new file mode 100644 index 0000000..0fd24ef Binary files /dev/null and b/start/img/output.gif differ diff --git a/start/install/index.html b/start/install/index.html new file mode 100644 index 0000000..b1d3db2 --- /dev/null +++ b/start/install/index.html @@ -0,0 +1,1106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Installation - QGIS Animation Workbench + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Installing the QGIS Animation Workbench plugin

+

⚠️ Please take note: We have not yet published the plugin in the QGIS Plugin Repository, but when we do you will be able to access it simply by clicking on the "QGIS Animation Workbench" option in the QGIS Plugin Manager.

+

Manual install from GitHub (tagged release)

+

To install, visit the Github Repository, click on the Actions tab, and click on the +Make QGIS Plugin Zip For Manual Installs workflow (the bottom one).

+

Install 0000

+

Click on the most recent workflow run (the top one).

+

Install 0001

+

Scroll down on the on the page.

+

Install 0002

+

And click on animation_workbench to download the most recent build of the plugin

+

Install 0003

+

Download the animation_workbench.zip file and open it in QGIS using the plugin manager as described below.

+
    +
  1. Open QGIS
  2. +
  3. Plugins ➡ Manage and install plugins ...
  4. +
  5. Choose the Install from zip tab +image
  6. +
  7. Select the animation_workbench.zip download
  8. +
  9. Click the Install Plugin button.
  10. +
+

Install from plugin manager

+

⚠️ Please take note: We have not yet published the plugin in the QGIS Plugin Repository, in the mean time please follow the steps above to do the installation.

+

To access the QGIS Plugin Manager you simply need to select +PluginsManage and Install Plugins... (1) in the Menu Toolbar.

+

Plugin Repository

+

Once the QGIS Plugin Manager loads, you need to navigate to the All (2) tab and +type "animation" into the search bar (3). Select QGIS Animation Workbench from the list +of available plugins and then select Install Plugin (4).

+

Search For and Install Plugin

+

Once the Animation Workbench is installed, you can access it by clicking on the +Animation Workbench icon (5) in the Plugin Toolbar.

+

Launch the Workbench

+
+

Note if you are on Ubuntu, you may need to install the Qt5 multimedia libraries.

+
+
sudo apt install PyQt5.QtMultimedia
+
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/start/using/index.html b/start/using/index.html new file mode 100644 index 0000000..cff2666 --- /dev/null +++ b/start/using/index.html @@ -0,0 +1,1152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Using the Workbench - QGIS Animation Workbench + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Using the Animation Workbench

+

In this section, we describe the general workflow for using the Animation Workbench.

+

Process Overview

+
    +
  1. Create a QGIS project!
  2. +
  3. Identify features that will be animated.
  4. +
  5. Use the QGIS Expressions system with the variables introduced by the Animation + Workbench to define behaviours of your symbols during flight and hover modes of your + animation.
  6. +
  7. Open the Animation Workbench and configure your animation, choosing between the + different modes and options.
  8. +
  9. Render your animation!
  10. +
+

More in Depth Process

+

1. Create a QGIS Project +  +Open QGIS and click on ProjectNew

+

New Project

+

 

+

Add new layers to your project

+

Add Layers

+
+

A simple way to add a base layer is to type "world" (1) into the coordinate +textbox

+
+

Style the layers you've added to make your project look a bit better. Select the +layer (2) you want to style and in the Layer Styling toolbar (3), style the layer to +look appealing to you.

+

Style Layers

+

 

+

2. Identify features that will be animated. + 

+

Pick the layer (or layers) that you want to animate. Then either find or create the +animation for the layer. Make sure you have all the correct attribution for any +animations you use. Below is an example of an animation split into its frames.

+

Animation Frames

+

3. Use the QGIS Expressions system with the variables introduced by the Animation +Workbench to define behaviours of your symbols during flight and hover modes of your +animation. + 

+

Select the layer you want to animate and open the Layer Styling toolbar.

+
+

If you are using QGIS 3.26 you can simply use the new animated point symbol, +or if you're using an older version of QGIS 3.x follow the instructions below.

+
+

The layer should be a Raster Image Marker. Once you have selected the image you +want to use click on the QGIS Expressions dropdown menu (4) and click on Edit (5).

+

Edit Expression

+

  +Use the Code Snippets Section for more in depth help. The +example below works with the bird animation from earlier

+

Expression Snippet

+
   @project_home
+   ||
+   '/bird/bird_00'
+   || 
+   lpad(to_string(@frame_number % 9), 2, '0')
+   || 
+   '.png'
+
+

1. Open the Animation Workbench and configure your animation, choosing between the +different modes and options. + 

+

Open the Workbench by clicking the Animation Workbench (6) icon in the Plugin Toolbar.

+

Open Workbench

+

Configure the settings for your animation. The screenshot below is configured for +the example presented in this section. The Animation Layer is selected as route (7) +because that is the path the animation will fly along, the Zoom Range (8) was selected +from the Map Canvas Extent, and the Frame rate per second (9) was set to 9 to match +the bird animation.

+

Output Setup

+

Set your desired Output Options (10) Select a location for your output (11).

+

Output Location

+
+

Refer to the Workbench User Interface Section for more information about +what various settings and buttons accomplish.

+
+

2. Render your animation! +  +Click Run and render your output. The output below is the output from the example.

+

Output

+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/tutorials/img/tut_1/001_InitialOpen_1.png b/tutorials/img/tut_1/001_InitialOpen_1.png new file mode 100644 index 0000000..b7a0fd3 Binary files /dev/null and b/tutorials/img/tut_1/001_InitialOpen_1.png differ diff --git a/tutorials/img/tut_1/002_AddSymbolLayer_1.png b/tutorials/img/tut_1/002_AddSymbolLayer_1.png new file mode 100644 index 0000000..00bbbd8 Binary files /dev/null and b/tutorials/img/tut_1/002_AddSymbolLayer_1.png differ diff --git a/tutorials/img/tut_1/003_StyleSymbolLayer_1.png b/tutorials/img/tut_1/003_StyleSymbolLayer_1.png new file mode 100644 index 0000000..828fe36 Binary files /dev/null and b/tutorials/img/tut_1/003_StyleSymbolLayer_1.png differ diff --git a/tutorials/img/tut_1/004_SymbolLayerSettings_1.png b/tutorials/img/tut_1/004_SymbolLayerSettings_1.png new file mode 100644 index 0000000..cf4afde Binary files /dev/null and b/tutorials/img/tut_1/004_SymbolLayerSettings_1.png differ diff --git a/tutorials/img/tut_1/005_EditExpression_1.png b/tutorials/img/tut_1/005_EditExpression_1.png new file mode 100644 index 0000000..aaf130b Binary files /dev/null and b/tutorials/img/tut_1/005_EditExpression_1.png differ diff --git a/tutorials/img/tut_1/006_OffsetSnippet_1.png b/tutorials/img/tut_1/006_OffsetSnippet_1.png new file mode 100644 index 0000000..f02dbc5 Binary files /dev/null and b/tutorials/img/tut_1/006_OffsetSnippet_1.png differ diff --git a/tutorials/img/tut_1/007_AnimationPlan_1.png b/tutorials/img/tut_1/007_AnimationPlan_1.png new file mode 100644 index 0000000..bbfd90b Binary files /dev/null and b/tutorials/img/tut_1/007_AnimationPlan_1.png differ diff --git a/tutorials/img/tut_1/008_OutputTab_1.png b/tutorials/img/tut_1/008_OutputTab_1.png new file mode 100644 index 0000000..f93512c Binary files /dev/null and b/tutorials/img/tut_1/008_OutputTab_1.png differ diff --git a/tutorials/img/tut_1/output.gif b/tutorials/img/tut_1/output.gif new file mode 100644 index 0000000..b7f6324 Binary files /dev/null and b/tutorials/img/tut_1/output.gif differ diff --git a/tutorials/img/tut_2/001_ChooseCRS_1.png b/tutorials/img/tut_2/001_ChooseCRS_1.png new file mode 100644 index 0000000..94852fe Binary files /dev/null and b/tutorials/img/tut_2/001_ChooseCRS_1.png differ diff --git a/tutorials/img/tut_2/002_AddBackingLayers_1.png b/tutorials/img/tut_2/002_AddBackingLayers_1.png new file mode 100644 index 0000000..c068252 Binary files /dev/null and b/tutorials/img/tut_2/002_AddBackingLayers_1.png differ diff --git a/tutorials/img/tut_2/003_ZoomToDam_1.png b/tutorials/img/tut_2/003_ZoomToDam_1.png new file mode 100644 index 0000000..8fff6e7 Binary files /dev/null and b/tutorials/img/tut_2/003_ZoomToDam_1.png differ diff --git a/tutorials/img/tut_2/004_AddNewLayer_1.png b/tutorials/img/tut_2/004_AddNewLayer_1.png new file mode 100644 index 0000000..8b8fbb9 Binary files /dev/null and b/tutorials/img/tut_2/004_AddNewLayer_1.png differ diff --git a/tutorials/img/tut_2/005_LayerSettings_1.png b/tutorials/img/tut_2/005_LayerSettings_1.png new file mode 100644 index 0000000..58d30d0 Binary files /dev/null and b/tutorials/img/tut_2/005_LayerSettings_1.png differ diff --git a/tutorials/img/tut_2/006_EditLayerPoints_1.png b/tutorials/img/tut_2/006_EditLayerPoints_1.png new file mode 100644 index 0000000..b57e1a6 Binary files /dev/null and b/tutorials/img/tut_2/006_EditLayerPoints_1.png differ diff --git a/tutorials/img/tut_2/007_AddedPoints_1.png b/tutorials/img/tut_2/007_AddedPoints_1.png new file mode 100644 index 0000000..2a648da Binary files /dev/null and b/tutorials/img/tut_2/007_AddedPoints_1.png differ diff --git a/tutorials/img/tut_2/008_BothLayersAdded_1.png b/tutorials/img/tut_2/008_BothLayersAdded_1.png new file mode 100644 index 0000000..b2143fc Binary files /dev/null and b/tutorials/img/tut_2/008_BothLayersAdded_1.png differ diff --git a/tutorials/img/tut_2/009_ChangetoRIM_1.png b/tutorials/img/tut_2/009_ChangetoRIM_1.png new file mode 100644 index 0000000..44d1358 Binary files /dev/null and b/tutorials/img/tut_2/009_ChangetoRIM_1.png differ diff --git a/tutorials/img/tut_2/010_SelectImage_1.png b/tutorials/img/tut_2/010_SelectImage_1.png new file mode 100644 index 0000000..a0e84a1 Binary files /dev/null and b/tutorials/img/tut_2/010_SelectImage_1.png differ diff --git a/tutorials/img/tut_2/011_ChangeSizeToMAS_1.png b/tutorials/img/tut_2/011_ChangeSizeToMAS_1.png new file mode 100644 index 0000000..c9eec45 Binary files /dev/null and b/tutorials/img/tut_2/011_ChangeSizeToMAS_1.png differ diff --git a/tutorials/img/tut_2/012_EditExpression_1.png b/tutorials/img/tut_2/012_EditExpression_1.png new file mode 100644 index 0000000..dbe857a Binary files /dev/null and b/tutorials/img/tut_2/012_EditExpression_1.png differ diff --git a/tutorials/img/tut_2/013_FishExpression_1.png b/tutorials/img/tut_2/013_FishExpression_1.png new file mode 100644 index 0000000..c8879a1 Binary files /dev/null and b/tutorials/img/tut_2/013_FishExpression_1.png differ diff --git a/tutorials/img/tut_2/014_BirdExpression_1.png b/tutorials/img/tut_2/014_BirdExpression_1.png new file mode 100644 index 0000000..0783206 Binary files /dev/null and b/tutorials/img/tut_2/014_BirdExpression_1.png differ diff --git a/tutorials/img/tut_2/015_AnimationPlan_1.png b/tutorials/img/tut_2/015_AnimationPlan_1.png new file mode 100644 index 0000000..ff9feb4 Binary files /dev/null and b/tutorials/img/tut_2/015_AnimationPlan_1.png differ diff --git a/tutorials/img/tut_2/016_Output_1.png b/tutorials/img/tut_2/016_Output_1.png new file mode 100644 index 0000000..ed4ba61 Binary files /dev/null and b/tutorials/img/tut_2/016_Output_1.png differ diff --git a/tutorials/img/tut_2/tut_2_output.gif b/tutorials/img/tut_2/tut_2_output.gif new file mode 100644 index 0000000..a1a2cdd Binary files /dev/null and b/tutorials/img/tut_2/tut_2_output.gif differ diff --git a/tutorials/img/tut_3/001_InitialOpen_1.png b/tutorials/img/tut_3/001_InitialOpen_1.png new file mode 100644 index 0000000..8816aca Binary files /dev/null and b/tutorials/img/tut_3/001_InitialOpen_1.png differ diff --git a/tutorials/img/tut_3/002_CreateNewGpkg_1.png b/tutorials/img/tut_3/002_CreateNewGpkg_1.png new file mode 100644 index 0000000..6aac83e Binary files /dev/null and b/tutorials/img/tut_3/002_CreateNewGpkg_1.png differ diff --git a/tutorials/img/tut_3/003_CreateNewLayer_1.png b/tutorials/img/tut_3/003_CreateNewLayer_1.png new file mode 100644 index 0000000..b49c2a0 Binary files /dev/null and b/tutorials/img/tut_3/003_CreateNewLayer_1.png differ diff --git a/tutorials/img/tut_3/004_1_ToggleEditing_1.png b/tutorials/img/tut_3/004_1_ToggleEditing_1.png new file mode 100644 index 0000000..32610c7 Binary files /dev/null and b/tutorials/img/tut_3/004_1_ToggleEditing_1.png differ diff --git a/tutorials/img/tut_3/004_2_PointsAdded_1.png b/tutorials/img/tut_3/004_2_PointsAdded_1.png new file mode 100644 index 0000000..28767bc Binary files /dev/null and b/tutorials/img/tut_3/004_2_PointsAdded_1.png differ diff --git a/tutorials/img/tut_3/005_AddSymbolLayer_1.png b/tutorials/img/tut_3/005_AddSymbolLayer_1.png new file mode 100644 index 0000000..e5e5e73 Binary files /dev/null and b/tutorials/img/tut_3/005_AddSymbolLayer_1.png differ diff --git a/tutorials/img/tut_3/006_GeometryGenerator_1.png b/tutorials/img/tut_3/006_GeometryGenerator_1.png new file mode 100644 index 0000000..f8a0e1f Binary files /dev/null and b/tutorials/img/tut_3/006_GeometryGenerator_1.png differ diff --git a/tutorials/img/tut_3/007_GeomLine_1.png b/tutorials/img/tut_3/007_GeomLine_1.png new file mode 100644 index 0000000..8aeaab3 Binary files /dev/null and b/tutorials/img/tut_3/007_GeomLine_1.png differ diff --git a/tutorials/img/tut_3/008_MarkerLine_1.png b/tutorials/img/tut_3/008_MarkerLine_1.png new file mode 100644 index 0000000..95f3036 Binary files /dev/null and b/tutorials/img/tut_3/008_MarkerLine_1.png differ diff --git a/tutorials/img/tut_3/009_MarkerLineSymbol_1.png b/tutorials/img/tut_3/009_MarkerLineSymbol_1.png new file mode 100644 index 0000000..0b7cc74 Binary files /dev/null and b/tutorials/img/tut_3/009_MarkerLineSymbol_1.png differ diff --git a/tutorials/img/tut_3/010_AddGeometry_1.png b/tutorials/img/tut_3/010_AddGeometry_1.png new file mode 100644 index 0000000..c234470 Binary files /dev/null and b/tutorials/img/tut_3/010_AddGeometry_1.png differ diff --git a/tutorials/img/tut_3/011_EditMarkerLine_1.png b/tutorials/img/tut_3/011_EditMarkerLine_1.png new file mode 100644 index 0000000..53fe8a2 Binary files /dev/null and b/tutorials/img/tut_3/011_EditMarkerLine_1.png differ diff --git a/tutorials/img/tut_3/012_OffsetExpression_1.png b/tutorials/img/tut_3/012_OffsetExpression_1.png new file mode 100644 index 0000000..c94c37e Binary files /dev/null and b/tutorials/img/tut_3/012_OffsetExpression_1.png differ diff --git a/tutorials/img/tut_3/013_EditMarkerSymbol_1.png b/tutorials/img/tut_3/013_EditMarkerSymbol_1.png new file mode 100644 index 0000000..35420e8 Binary files /dev/null and b/tutorials/img/tut_3/013_EditMarkerSymbol_1.png differ diff --git a/tutorials/img/tut_3/014_MarkerExpression_1.png b/tutorials/img/tut_3/014_MarkerExpression_1.png new file mode 100644 index 0000000..b571021 Binary files /dev/null and b/tutorials/img/tut_3/014_MarkerExpression_1.png differ diff --git a/tutorials/img/tut_3/015_OpenAW_1.png b/tutorials/img/tut_3/015_OpenAW_1.png new file mode 100644 index 0000000..4bf44db Binary files /dev/null and b/tutorials/img/tut_3/015_OpenAW_1.png differ diff --git a/tutorials/img/tut_3/016_AnimationPlan_1.png b/tutorials/img/tut_3/016_AnimationPlan_1.png new file mode 100644 index 0000000..161e527 Binary files /dev/null and b/tutorials/img/tut_3/016_AnimationPlan_1.png differ diff --git a/tutorials/img/tut_3/017_Output_1.png b/tutorials/img/tut_3/017_Output_1.png new file mode 100644 index 0000000..585d6ce Binary files /dev/null and b/tutorials/img/tut_3/017_Output_1.png differ diff --git a/tutorials/img/tut_3/tutorial_3_output_1.gif b/tutorials/img/tut_3/tutorial_3_output_1.gif new file mode 100644 index 0000000..0018535 Binary files /dev/null and b/tutorials/img/tut_3/tutorial_3_output_1.gif differ diff --git a/tutorials/index.html b/tutorials/index.html new file mode 100644 index 0000000..6afdd26 --- /dev/null +++ b/tutorials/index.html @@ -0,0 +1,1327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + QGIS - QGIS Animation Workbench + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

QGIS

+ +

Tutorial 1: Point Along A Line

+

This tutorial introduces the concept of moving a point along a line within your animated +map.

+

1. Download and extract the Required Tutorial Zip Folder

+

2. Open the tutorial_1.qgz project file that is in the folder. When you first open it you +you see something like this:

+

Initial Open

+

3. Select the premade line layer (1), and click on the Add Symbol Layer +(green plus symbol) button (2) to it.

+

Add Symbol Layer

+

Change the new Symbol Layer (3) type to marker line and then style it (4) so that it is more visible.

+

Style Symbol Layer

+

4. Change the Symbol Layer's settings so that the point is only on the first vertex (5) and +and not at equidistant intervals.

+

Change the offset along the line to be Percentage (6).

+

Symbol Layer Settings

+

Click the Dropdown Menu (7) ➔Edit... (8) and then add the following code snippet

+

Edit Expression

+
    -- Point Along Line Code Snippet
+    (@current_hover_frame/@hover_frames) * 100
+
+

Offset along line Snippet

+

The snippet tells QGIS how far along the line (as a percentage of the line length) to +render the point in each frame.

+

5. Open the Workbench and select Fixed Extent (9).

+

Click on Map Canvas Extent (10) and set the the Frames to 300 (11) (for a 10 second +output at 30 frames per second).

+

Animation Plan

+

6. Skip over the Intro, Outro, and Soundtrack tabs. In the Output tab, set the output +format (12) and resolution (13), and set the output location's path (14).

+

Output Tab

+

7. Click Run and render your output.

+

Point Along Line Output GIF

+

After this tutorial you should have a better idea of how to make a point move along a line. +An expansion to this example would be to make the moving point a dynamically changing +marker (like the markers in tutorial 1). Go have fun!

+

Tutorial 2: Basic Dynamically Changing Markers

+

This tutorial aims to show you the basics of creating, and animating, a static layer to use +with the Animation Workbench. There are three pre-made layers to allow the main focus of +the tutorial to be on the Animation Workbench and not on QGIS as a whole.

+

1. Download and extract the Required Tutorial Zip Folder

+

2. Open the tutorial_2.qgz project file that is in the folder. + 

+

3. Set the CRS of your project to WGS84/UTM zone 35S (EPSG: 32735).

+

Choose CRS

+

4. In the Browser, expand the tutorial_2.gpkg and add the three pre-made layers +(VaalDam, SouthAfrica, and route) (A) to your project.

+

Add Backing Layers

+

5. In the Layers Panel, arrange the layers in the following order: route, VaalDam, +SouthAfrica. Then right-click on the VaalDam layer and Zoom to Layer(s) (B)

+

Zoom to Dam

+

Style the three layers to your preferred style. + 

+

6. Now create a new layer in the tutorial_2.gpkg by clicking LayerCreate Layer➔ +New GeoPackage Layer... (C).

+

Create New Layer

+

Click on the Ellipsis (D), navigate to and select the tutorial_2.gpkg, and +click Save. Change the Table name to fish (E), set the Geometry type as Point (F), and +change the CRS to match the Project CRS (G).

+

Layer Settings

+

Click on OK and then click Add New Layer on the window that pops up. + 

+

7. Select the fish layer and then click on Toggle EditingAdd Point Feature (H).

+

Edit Layer Points

+

Add a few points wherever you feel they should go (Hint: This is a fish layer so adding +them above the dam layer would be best). Don't worry about naming the points, just add +them.

+

Added Points

+

Save your changes by clicking on Save Layer Edits just next to the Toggle Editing +button. Then stop editing the layer. + 

+

8. Repeat steps 6. and 7. but change the Table name to bird and add the points over +the land areas.

+

Both Layers Added

+

9. Select the fish layer and then in the Layer styling toolbar (I) change the +Symbol layer type to Raster Image Marker (J).

+

Change to RIM

+

Select the marker image by clicking the Dropdown menuSelect File... (K) +and then choosing fishfish_0000.png.

+

Select Image

+

Click Open

+

10. Change the marker's Size Unit to Meters at Scale (L)

+

Change Size to MAS

+

and set the Width and Height to 1000. + 

+

11. Repeat Steps 9. and 10. with the bird layer but instead choosing birdbird_0000.png +and setting the Width and Height to 3000.

+

 

+
+

In QGIS 3.26, or later, the Symbol layer type can simply be selected as +Animated Marker and Step 12. can be skipped.

+
+

12. To animate the fish and bird layers using the QGIS Expressions system click the +Dropdown MenuEdit... (M).

+

Edit Expression

+

For the fish layer use the following expression:

+

Fish Expression

+
    @project_home
+    || 
+    '/fish/fish_00'
+    ||
+    lpad(to_string( @frame_number % 32), 2, '0')
+    ||
+    '.png'
+
+

And for the bird layer use:

+

Bird Expression

+
    @project_home
+    ||
+    '/bird/bird_00'
+    || 
+    lpad(to_string(@frame_number % 9), 2, '0')
+    || 
+    '.png'
+
+
+

Refer to the What is the Workbench doing? section for an explanation + about what the above code snippet is doing.

+
+

13. Open the Animation Workbench (refer to the Using the Animation Workbench section +if you are unsure how to open the Workbench).

+

In the Animation Plan tab set:

+
    +
  • the Render Mode to Planar (N),
  • +
  • the Animation Layer to route (O) using the dropdown menu,
  • +
  • the Zoom Range (P) to 1:270000 for the Minimum and 1:135000 for the Maximum,
  • +
  • the Frame rate per second to 9 fps (Q),
  • +
  • the Travel duration to 4,00 s (R),
  • +
  • and the Feature hover duration to 2,00 s (S)
  • +
+

Enable both the Pan and Zoom easings and set them to linear.

+

Animation Plan

+

14. Skip past the Intro, Outro, and Soundtrack tabs to the Output tab. Set the +Output Format as Animated Gif (T) and the Output Resolution to +720p (1280x720) (U). The Output Resolution can be set as any of the three +choices but was set at 720 for this tutorial for the sake of speed. Set the output +location to one you can find easily (V)

+

Output Tab

+

15. Click Run and watch what the Workbench is doing in the Progress tab. Once the +Workbench is finished running, you should end up with an output similar to this:

+

Output GIF

+

After this tutorial you should have a better understanding of how to create a point layer +in your project and then to change the Single Symbol markers into stationary animated +markers. A key focus is the idea that you can tell versions of QGIS before 3.26 to dynamically +change markers using short code snippets. Versions of QGIS post 3.26 allow a user to +simply use the Animated Marker feature without editing an expression.

+

Tutorial 3: Flying Points

+

This tutorial aims to show you how add a flying point animation to points on your map using +built-in QGIS functionalities (The geometry generator line) and introduced variables from +the workbench.

+

1. Download and extract the Required Tutorial Zip Folder

+

2. Open the tutorial_3.qgz project file. +When you first open the project file you should be greeted with something like this:

+

Initial Open

+

3. Create a new point layer in a new geopackage by clicking LayerCreate Layer➔ + New GeoPackage Layer.... Click on the Ellipsis (three dots) next to the Database +textbox and navigate to the folder that the tutorial_3.qgz file is located in. Type the File +name "tutorial_3" (1) and ensure the file will be saved as a GeoPackage (2) and click +Save (3).

+

Create New GeoPackage

+

Change the Table name to flying_points (4), set the Geometry type as Point (5) and +change the CRS to match the Project CRS (6).

+

Create New layer

+

Click OK (7) + 

+

4. Click on Toggle EditingAdd Point Feature (8).

+

Toggle Editing

+

And randomly add points to your map. Depending on your computer's capabilites, you +can add more, or fewer, points than the example below.

+

Points Added

+

Save your Layer Edits and toggle off the Editing tool. + 

+

5. Style the points layer.

+

Select the flying_points (9) layer and in the Layer Styling toolbar click on the +Add Symbol Layer (green plus symbol) button (10).

+

Add Symbol Layer

+

Select the top Simple Marker (11) and change its Symbol layer type to +Geometry Generator (12)

+

Geometry Generator

+

and then set the Geometry type to LineString / MultiLineString (13).

+

Geometry Line

+

Change the line's Symbol layer type to Marker Line (14).

+

Marker Line

+

Add a second Simple marker to the marker line so that you end up with something like +this:

+

Marker Line Symbol

+

Style the various Simple Markers to your preferred look.

+

 

+

6. Select the Geometry Generator symbol layer (15) and add this code to it:

+
    wave_randomized(
+    make_line(
+    $geometry, geometry(@hover_feature)), 
+    100, 1000, 1000, 10000, 1)
+
+

Add Geometry

+
+

More information about what changing the numbers will affect can be found in the +QGIS expressions editor. + 

+
+

7. A few options need to be changed in the Marker Line symbol layer (16): The Marker +placement needs to be set to On first vertex (17) and, the Offset along line needs to be +changed to Percentage (18). The click the Dropdown menu next to Offset along line and +select Edit... (19).

+

Edit Marker Line

+

In the Expression String Builder add the following code snippet:

+
    100 - to_int((@current_hover_frame / @hover_frames) * 100 )
+
+

Offset Expression

+

Click OK

+

8. Select the first Simple Marker symbol layer (20) in the Marker Line symbol layer. Scroll +down to Offset and click on the Dropdown MenuEdit.. (21).

+

Edit Marker Symbol

+

In the Expression String Builder add the following code snippet:

+
    -- Taken from https://spicyyoghurt.com/tools/easing-functions
+    --    t = Time - Amount of time that has passed since the beginning of the animation. Usually starts at 0 and is slowly increased using a game loop or other update function.
+    --    b = Beginning value - The starting point of the animation. Usually it's a static value, you can start at 0 for example.
+    --    c = Change in value - The amount of change needed to go from starting point to end point. It's also usually a static value.
+    --    d = Duration - Amount of time the animation will take. Usually a static value aswell.
+    -- Sinusoidal
+    -- -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
+
+    -- Use with the animation in static mode
+    if(@hover_feature_id != $id,
+    array(
+        (-@hover_frames / 2) * (cos( (pi() * @frame_number / @hover_frames ) - 1)) ,
+        (-@hover_frames / 2) * (sin( (pi() * @frame_number / @hover_frames ) - 1)) 
+        ),
+        array (0,0))
+
+

Marker Expression

+

Click OK

+

9. Open the Animation Workbench (22)

+

Open AW

+

10. Set up the Animation Plan with:

+
    +
  • the Render Mode to Planar (23),
  • +
  • the Animation Layer to flying_points (24) using the dropdown menu,
  • +
  • the Zoom Range (25) to 1:22000000 for the Minimum and 1:11000000 for the + Maximum,
  • +
  • the Frame rate per second to 9 fps (26),
  • +
  • the Travel duration to 2,00 s (27),
  • +
  • the Feature hover duration to 2,00 s (28),
  • +
  • and the Zoom Easing as InCirc (29)
  • +
+

Animation Plan

+
+

With a decently specced computer you can up the fps and get the points to fly +faster in your output. + 

+
+

11. Add license-free media to the Intro, Outro, and Soundtrack.

+
+

Make sure your Soundtrack is as long as, or longer than, your final animation will be +(including the Intro, Animation, and Outro).

+
+

 

+

12. Set the Output Format as Movie (MP4) (30) and the Output Resolution to +1080 (1920x1080) (31). The Output Resolution can be set as any of the three +choices but was set at 1080 for this tutorial for the sake of speed. Set the output +location (32) to one you can easily locate.

+

Output

+

13. Click Run and get an output. The GIF below is the visual output of the tutorial if you +followed step-by-step and set the parameters to exactly what was stated.

+

Output Gif

+

The link to a more complex output (with an Intro, an Outro, and a Soundtrack) can +be found here

+

 

+

After this tutorial you should have a better idea of how you can use a mixture of built-in +QGIS functionalites and the workbench's introduced variables to generate interesting outputs.

+ + + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file