diff --git a/example/Images/__init__.py b/example/Images/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..85fa7c9a6f20c424b4a453563955cd63736d93a3
--- /dev/null
+++ b/example/Images/__init__.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python
+"""
+This file is auto-generated.
+Do not edit manually, as changes might get overwritten.
+"""
+__author__ = "auto-generated by make\make.py"
+__date__ = "2017-02-17T11:36:22"
+
+import sys, os
+
+thisDir = os.path.dirname(__file__)
+# File path attributes:
+# Raster files:
+Img_2012_04_07_LE72270652012098EDC00_BOA = os.path.join(thisDir,r'2012-04-07_LE72270652012098EDC00_BOA.bsq')
+Img_2012_04_23_LE72270652012114EDC00_BOA = os.path.join(thisDir,r'2012-04-23_LE72270652012114EDC00_BOA.bsq')
+Img_2012_05_09_LE72270652012130EDC00_BOA = os.path.join(thisDir,r'2012-05-09_LE72270652012130EDC00_BOA.bsq')
+Img_2012_05_25_LE72270652012146EDC00_BOA = os.path.join(thisDir,r'2012-05-25_LE72270652012146EDC00_BOA.bsq')
+Img_2012_06_10_LE72270652012162EDC00_BOA = os.path.join(thisDir,r'2012-06-10_LE72270652012162EDC00_BOA.bsq')
+Img_2012_06_26_LE72270652012178EDC00_BOA = os.path.join(thisDir,r'2012-06-26_LE72270652012178EDC00_BOA.bsq')
+Img_2012_07_12_LE72270652012194EDC00_BOA = os.path.join(thisDir,r'2012-07-12_LE72270652012194EDC00_BOA.bsq')
+Img_2012_07_28_LE72270652012210EDC00_BOA = os.path.join(thisDir,r'2012-07-28_LE72270652012210EDC00_BOA.bsq')
+Img_2012_08_29_LE72270652012242CUB00_BOA = os.path.join(thisDir,r'2012-08-29_LE72270652012242CUB00_BOA.bsq')
+Img_2012_09_14_LE72270652012258EDC00_BOA = os.path.join(thisDir,r'2012-09-14_LE72270652012258EDC00_BOA.bsq')
+Img_2012_09_30_LE72270652012274CUB00_BOA = os.path.join(thisDir,r'2012-09-30_LE72270652012274CUB00_BOA.bsq')
+Img_2012_10_16_LE72270652012290CUB00_BOA = os.path.join(thisDir,r'2012-10-16_LE72270652012290CUB00_BOA.bsq')
+Img_2012_11_01_LE72270652012306CUB02_BOA = os.path.join(thisDir,r'2012-11-01_LE72270652012306CUB02_BOA.bsq')
+Img_2013_02_05_LE72270652013036CUB00_BOA = os.path.join(thisDir,r'2013-02-05_LE72270652013036CUB00_BOA.bsq')
+Img_2013_05_12_LE72270652013132CUB00_BOA = os.path.join(thisDir,r'2013-05-12_LE72270652013132CUB00_BOA.bsq')
+Img_2013_05_20_LC82270652013140LGN01_BOA = os.path.join(thisDir,r'2013-05-20_LC82270652013140LGN01_BOA.bsq')
+Img_2013_06_05_LC82270652013156LGN00_BOA = os.path.join(thisDir,r'2013-06-05_LC82270652013156LGN00_BOA.bsq')
+Img_2013_06_21_LC82270652013172LGN00_BOA = os.path.join(thisDir,r'2013-06-21_LC82270652013172LGN00_BOA.bsq')
+Img_2013_06_29_LE72270652013180CUB08_BOA = os.path.join(thisDir,r'2013-06-29_LE72270652013180CUB08_BOA.bsq')
+Img_2013_07_07_LC82270652013188LGN00_BOA = os.path.join(thisDir,r'2013-07-07_LC82270652013188LGN00_BOA.bsq')
+Img_2013_07_15_LE72270652013196CUB00_BOA = os.path.join(thisDir,r'2013-07-15_LE72270652013196CUB00_BOA.bsq')
+Img_2013_07_23_LC82270652013204LGN00_BOA = os.path.join(thisDir,r'2013-07-23_LC82270652013204LGN00_BOA.bsq')
+Img_2013_07_31_LE72270652013212CUB00_BOA = os.path.join(thisDir,r'2013-07-31_LE72270652013212CUB00_BOA.bsq')
+Img_2013_08_08_LC82270652013220LGN00_BOA = os.path.join(thisDir,r'2013-08-08_LC82270652013220LGN00_BOA.bsq')
+Img_2013_08_16_LE72270652013228CUB00_BOA = os.path.join(thisDir,r'2013-08-16_LE72270652013228CUB00_BOA.bsq')
+Img_2013_08_24_LC82270652013236LGN00_BOA = os.path.join(thisDir,r'2013-08-24_LC82270652013236LGN00_BOA.bsq')
+Img_2013_09_01_LE72270652013244CUB00_BOA = os.path.join(thisDir,r'2013-09-01_LE72270652013244CUB00_BOA.bsq')
+Img_2013_09_09_LC82270652013252LGN00_BOA = os.path.join(thisDir,r'2013-09-09_LC82270652013252LGN00_BOA.bsq')
+Img_2013_09_17_LE72270652013260CUB01_BOA = os.path.join(thisDir,r'2013-09-17_LE72270652013260CUB01_BOA.bsq')
+Img_2013_10_03_LE72270652013276CUB00_BOA = os.path.join(thisDir,r'2013-10-03_LE72270652013276CUB00_BOA.bsq')
+Img_2013_10_11_LC82270652013284LGN00_BOA = os.path.join(thisDir,r'2013-10-11_LC82270652013284LGN00_BOA.bsq')
+Img_2013_12_06_LE72270652013340CUB00_BOA = os.path.join(thisDir,r'2013-12-06_LE72270652013340CUB00_BOA.bsq')
+Img_2013_12_14_LC82270652013348LGN00_BOA = os.path.join(thisDir,r'2013-12-14_LC82270652013348LGN00_BOA.bsq')
+Img_2014_01_15_LC82270652014015LGN00_BOA = os.path.join(thisDir,r'2014-01-15_LC82270652014015LGN00_BOA.bsq')
+Img_2014_03_20_LC82270652014079LGN00_BOA = os.path.join(thisDir,r'2014-03-20_LC82270652014079LGN00_BOA.bsq')
+Img_2014_04_21_LC82270652014111LGN00_BOA = os.path.join(thisDir,r'2014-04-21_LC82270652014111LGN00_BOA.bsq')
+Img_2014_04_29_LE72270652014119CUB00_BOA = os.path.join(thisDir,r'2014-04-29_LE72270652014119CUB00_BOA.bsq')
+Img_2014_05_07_LC82270652014127LGN00_BOA = os.path.join(thisDir,r'2014-05-07_LC82270652014127LGN00_BOA.bsq')
+Img_2014_05_15_LE72270652014135CUB00_BOA = os.path.join(thisDir,r'2014-05-15_LE72270652014135CUB00_BOA.bsq')
+Img_2014_05_23_LC82270652014143LGN00_BOA = os.path.join(thisDir,r'2014-05-23_LC82270652014143LGN00_BOA.bsq')
+Img_2014_05_31_LE72270652014151CUB00_BOA = os.path.join(thisDir,r'2014-05-31_LE72270652014151CUB00_BOA.bsq')
+Img_2014_06_08_LC82270652014159LGN00_BOA = os.path.join(thisDir,r'2014-06-08_LC82270652014159LGN00_BOA.bsq')
+Img_2014_06_16_LE72270652014167CUB00_BOA = os.path.join(thisDir,r'2014-06-16_LE72270652014167CUB00_BOA.bsq')
+Img_2014_06_24_LC82270652014175LGN00_BOA = os.path.join(thisDir,r'2014-06-24_LC82270652014175LGN00_BOA.bsq')
+Img_2014_07_02_LE72270652014183CUB00_BOA = os.path.join(thisDir,r'2014-07-02_LE72270652014183CUB00_BOA.bsq')
+Img_2014_07_10_LC82270652014191LGN00_BOA = os.path.join(thisDir,r'2014-07-10_LC82270652014191LGN00_BOA.bsq')
+Img_2014_07_18_LE72270652014199CUB00_BOA = os.path.join(thisDir,r'2014-07-18_LE72270652014199CUB00_BOA.bsq')
+Img_2014_07_26_LC82270652014207LGN00_BOA = os.path.join(thisDir,r'2014-07-26_LC82270652014207LGN00_BOA.bsq')
+Img_2014_08_03_LE72270652014215CUB00_BOA = os.path.join(thisDir,r'2014-08-03_LE72270652014215CUB00_BOA.bsq')
+Img_2014_08_11_LC82270652014223LGN00_BOA = os.path.join(thisDir,r'2014-08-11_LC82270652014223LGN00_BOA.bsq')
+Img_2014_08_19_LE72270652014231CUB00_BOA = os.path.join(thisDir,r'2014-08-19_LE72270652014231CUB00_BOA.bsq')
+Img_2014_08_27_LC82270652014239LGN00_BOA = os.path.join(thisDir,r'2014-08-27_LC82270652014239LGN00_BOA.bsq')
+Img_2014_09_04_LE72270652014247CUB00_BOA = os.path.join(thisDir,r'2014-09-04_LE72270652014247CUB00_BOA.bsq')
+Img_2014_09_12_LC82270652014255LGN00_BOA = os.path.join(thisDir,r'2014-09-12_LC82270652014255LGN00_BOA.bsq')
+Img_2014_09_20_LE72270652014263CUB00_BOA = os.path.join(thisDir,r'2014-09-20_LE72270652014263CUB00_BOA.bsq')
+Img_2014_09_28_LC82270652014271LGN00_BOA = os.path.join(thisDir,r'2014-09-28_LC82270652014271LGN00_BOA.bsq')
+Img_2014_10_06_LE72270652014279CUB00_BOA = os.path.join(thisDir,r'2014-10-06_LE72270652014279CUB00_BOA.bsq')
+Img_2014_10_14_LC82270652014287LGN00_BOA = os.path.join(thisDir,r'2014-10-14_LC82270652014287LGN00_BOA.bsq')
+Img_2014_11_07_LE72270652014311CUB00_BOA = os.path.join(thisDir,r'2014-11-07_LE72270652014311CUB00_BOA.bsq')
+Img_2014_11_15_LC82270652014319LGN00_BOA = os.path.join(thisDir,r'2014-11-15_LC82270652014319LGN00_BOA.bsq')
+Img_2014_12_17_LC82270652014351LGN00_BOA = os.path.join(thisDir,r'2014-12-17_LC82270652014351LGN00_BOA.bsq')
+Img_2015_01_02_LC82270652015002LGN00_BOA = os.path.join(thisDir,r'2015-01-02_LC82270652015002LGN00_BOA.bsq')
+Img_2015_01_10_LE72270652015010CUB00_BOA = os.path.join(thisDir,r'2015-01-10_LE72270652015010CUB00_BOA.bsq')
+Img_2015_01_18_LC82270652015018LGN00_BOA = os.path.join(thisDir,r'2015-01-18_LC82270652015018LGN00_BOA.bsq')
+Img_2015_02_03_LC82270652015034LGN00_BOA = os.path.join(thisDir,r'2015-02-03_LC82270652015034LGN00_BOA.bsq')
+Img_2015_02_27_LE72270652015058CUB04_BOA = os.path.join(thisDir,r'2015-02-27_LE72270652015058CUB04_BOA.bsq')
+Img_2015_03_15_LE72270652015074CUB00_BOA = os.path.join(thisDir,r'2015-03-15_LE72270652015074CUB00_BOA.bsq')
+Img_2015_03_23_LC82270652015082LGN00_BOA = os.path.join(thisDir,r'2015-03-23_LC82270652015082LGN00_BOA.bsq')
+Img_2015_03_31_LE72270652015090CUB00_BOA = os.path.join(thisDir,r'2015-03-31_LE72270652015090CUB00_BOA.bsq')
+Img_2015_04_08_LC82270652015098LGN00_BOA = os.path.join(thisDir,r'2015-04-08_LC82270652015098LGN00_BOA.bsq')
+Img_2015_04_24_LC82270652015114LGN00_BOA = os.path.join(thisDir,r'2015-04-24_LC82270652015114LGN00_BOA.bsq')
+Img_2015_05_10_LC82270652015130LGN00_BOA = os.path.join(thisDir,r'2015-05-10_LC82270652015130LGN00_BOA.bsq')
+Img_2015_05_18_LE72270652015138CUB00_BOA = os.path.join(thisDir,r'2015-05-18_LE72270652015138CUB00_BOA.bsq')
+Img_2015_05_26_LC82270652015146LGN00_BOA = os.path.join(thisDir,r'2015-05-26_LC82270652015146LGN00_BOA.bsq')
+Img_2015_06_03_LE72270652015154CUB00_BOA = os.path.join(thisDir,r'2015-06-03_LE72270652015154CUB00_BOA.bsq')
+Img_2015_06_11_LC82270652015162LGN00_BOA = os.path.join(thisDir,r'2015-06-11_LC82270652015162LGN00_BOA.bsq')
+Img_2015_06_19_LE72270652015170CUB00_BOA = os.path.join(thisDir,r'2015-06-19_LE72270652015170CUB00_BOA.bsq')
+Img_2015_06_27_LC82270652015178LGN00_BOA = os.path.join(thisDir,r'2015-06-27_LC82270652015178LGN00_BOA.bsq')
+Img_2015_07_13_LC82270652015194LGN00_BOA = os.path.join(thisDir,r'2015-07-13_LC82270652015194LGN00_BOA.bsq')
+Img_2015_07_21_LE72270652015202CUB00_BOA = os.path.join(thisDir,r'2015-07-21_LE72270652015202CUB00_BOA.bsq')
+Img_2015_07_29_LC82270652015210LGN00_BOA = os.path.join(thisDir,r'2015-07-29_LC82270652015210LGN00_BOA.bsq')
+Img_2015_08_06_LE72270652015218CUB00_BOA = os.path.join(thisDir,r'2015-08-06_LE72270652015218CUB00_BOA.bsq')
+Img_2015_08_14_LC82270652015226LGN00_BOA = os.path.join(thisDir,r'2015-08-14_LC82270652015226LGN00_BOA.bsq')
+Img_2015_08_22_LE72270652015234CUB00_BOA = os.path.join(thisDir,r'2015-08-22_LE72270652015234CUB00_BOA.bsq')
+Img_2015_08_30_LC82270652015242LGN00_BOA = os.path.join(thisDir,r'2015-08-30_LC82270652015242LGN00_BOA.bsq')
+Img_2015_09_07_LE72270652015250ASN00_BOA = os.path.join(thisDir,r'2015-09-07_LE72270652015250ASN00_BOA.bsq')
+Img_2015_09_15_LC82270652015258LGN00_BOA = os.path.join(thisDir,r'2015-09-15_LC82270652015258LGN00_BOA.bsq')
+Img_2015_09_23_LE72270652015266ASN00_BOA = os.path.join(thisDir,r'2015-09-23_LE72270652015266ASN00_BOA.bsq')
+Img_2015_10_01_LC82270652015274LGN00_BOA = os.path.join(thisDir,r'2015-10-01_LC82270652015274LGN00_BOA.bsq')
+Img_2015_10_09_LE72270652015282ASN00_BOA = os.path.join(thisDir,r'2015-10-09_LE72270652015282ASN00_BOA.bsq')
+Img_2015_10_17_LC82270652015290LGN00_BOA = os.path.join(thisDir,r'2015-10-17_LC82270652015290LGN00_BOA.bsq')
+Img_2015_10_25_LE72270652015298ASN00_BOA = os.path.join(thisDir,r'2015-10-25_LE72270652015298ASN00_BOA.bsq')
+Img_2015_11_02_LC82270652015306LGN00_BOA = os.path.join(thisDir,r'2015-11-02_LC82270652015306LGN00_BOA.bsq')
+Img_2015_11_10_LE72270652015314ASN00_BOA = os.path.join(thisDir,r'2015-11-10_LE72270652015314ASN00_BOA.bsq')
+re_2012_06_12 = os.path.join(thisDir,r're_2012-06-12.bsq')
+re_2012_07_24 = os.path.join(thisDir,r're_2012-07-24.bsq')
+re_2012_07_25 = os.path.join(thisDir,r're_2012-07-25.bsq')
+re_2012_08_05 = os.path.join(thisDir,r're_2012-08-05.bsq')
+re_2012_09_06 = os.path.join(thisDir,r're_2012-09-06.bsq')
+re_2013_05_18 = os.path.join(thisDir,r're_2013-05-18.bsq')
+re_2014_06_12 = os.path.join(thisDir,r're_2014-06-12.bsq')
+re_2014_06_25 = os.path.join(thisDir,r're_2014-06-25.bsq')
+re_2014_08_08 = os.path.join(thisDir,r're_2014-08-08.bsq')
+re_2014_08_10 = os.path.join(thisDir,r're_2014-08-10.bsq')
+re_2014_08_17 = os.path.join(thisDir,r're_2014-08-17.bsq')
+re_2014_08_20 = os.path.join(thisDir,r're_2014-08-20.bsq')
+re_2014_08_23 = os.path.join(thisDir,r're_2014-08-23.bsq')
+re_2014_08_25 = os.path.join(thisDir,r're_2014-08-25.bsq')
+re_2014_08_26 = os.path.join(thisDir,r're_2014-08-26.bsq')
+re_2014_10_10 = os.path.join(thisDir,r're_2014-10-10.bsq')
+re_2015_06_13 = os.path.join(thisDir,r're_2015-06-13.bsq')
+re_2015_06_19 = os.path.join(thisDir,r're_2015-06-19.bsq')
+re_2015_07_13 = os.path.join(thisDir,r're_2015-07-13.bsq')
+re_2015_07_17 = os.path.join(thisDir,r're_2015-07-17.bsq')
+re_2015_07_30 = os.path.join(thisDir,r're_2015-07-30.bsq')
+re_2015_08_04 = os.path.join(thisDir,r're_2015-08-04.bsq')
+re_2015_08_10 = os.path.join(thisDir,r're_2015-08-10.bsq')
+re_2015_09_18 = os.path.join(thisDir,r're_2015-09-18.bsq')
+
+
+
+# self-test to check each file path attribute
+for a in dir(sys.modules[__name__]):
+    v = getattr(sys.modules[__name__], a)
+    if type(v) == str and os.path.isabs(v):
+        if not os.path.exists(v):
+            sys.stderr.write('Missing package attribute file: {}={}'.format(a, v))
+
+# cleanup
+del thisDir, a, v
\ No newline at end of file
diff --git a/example/__init__.py b/example/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..fef66b5a2a716b02ab2baa874dc451f6ec8d472a
--- /dev/null
+++ b/example/__init__.py
@@ -0,0 +1 @@
+#!/usr/bin/env python
\ No newline at end of file
diff --git a/make/make.py b/make/make.py
index e0cd4883d729d737f99f4a48eec0e5e6e3a0e5fb..675208f1869c853b8c4d4a7d6444cca936cde8b6 100644
--- a/make/make.py
+++ b/make/make.py
@@ -17,6 +17,69 @@ from timeseriesviewer import DIR_UI, file_search
 jp = os.path.join
 
 
+def createFilePackage(dirData):
+    import numpy as np
+    from timeseriesviewer import DIR_REPO
+    pathInit = jp(dirData, '__init__.py')
+    code = ['#!/usr/bin/env python',
+            '"""',
+            'This file is auto-generated.',
+            'Do not edit manually, as changes might get overwritten.',
+            '"""',
+            '__author__ = "auto-generated by {}"'.format(os.path.relpath(__file__, DIR_REPO)),
+            '__date__ = "{}"'.format(np.datetime64('now')),
+            '',
+            'import sys, os',
+            '',
+            'thisDir = os.path.dirname(__file__)',
+            '# File path attributes:',
+            ]
+    files = file_search(dirData, '*', recursive=True)
+
+    filePathAttributes = set()
+    def addFiles(files, comment=None, numberPrefix='File'):
+        if len(files) > 0:
+            if comment:
+                code.append('# '+comment)
+            for f in files:
+                an, ext = os.path.splitext(os.path.basename(f))
+                if re.search('^\d', an):
+                    an = numberPrefix+an
+                an = re.sub(r'[-.]', '_',an)
+
+                assert an not in filePathAttributes
+                relpath = os.path.relpath(f, dirData)
+                code.append("{} = os.path.join(thisDir,r'{}')".format(an, relpath))
+                filePathAttributes.add(an)
+            code.append('\n')
+
+    raster = [f for f in files if re.search('.*\.(bsq|bip|bil|tif|tiff)$', f)]
+    vector = [f for f in files if re.search('.*\.(shp|kml|kmz)$', f)]
+
+    addFiles(raster, 'Raster files:', numberPrefix='Img_')
+    addFiles(vector, 'Vector files:', numberPrefix='Shp_')
+
+    #add self-test for file existence
+    if len(filePathAttributes) > 0:
+        code.extend(
+        [
+        "",
+        "# self-test to check each file path attribute",
+        "for a in dir(sys.modules[__name__]):",
+        "    v = getattr(sys.modules[__name__], a)",
+        "    if type(v) == str and os.path.isabs(v):" ,
+        "        if not os.path.exists(v):",
+        "            sys.stderr.write('Missing package attribute file: {}={}'.format(a, v))",
+        "",
+        "# cleanup",
+        "del thisDir ",
+        ]
+        )
+
+    open(pathInit, 'w').write('\n'.join(code))
+    print('Created '+pathInit)
+
+
 def getDOMAttributes(elem):
     assert isinstance(elem, QDomElement)
     values = dict()
@@ -374,6 +437,8 @@ def createCreditsHTML():
 if __name__ == '__main__':
     icondir = jp(DIR_UI, *['icons'])
     pathQrc = jp(DIR_UI,'resources.qrc')
+    from timeseriesviewer import DIR_EXAMPLES
+
     if False:
         from qgis import *
         from qgis.core import *
@@ -393,7 +458,8 @@ if __name__ == '__main__':
         qgsApp.setPrefixPath(PATH_QGS, True)
         qgsApp.initQgis()
 
-        pathDirTestData = r'C:\Users\geo_beja\Repositories\QGIS_Plugins\SenseCarbonTSViewer\example'
+
+        pathDirTestData = os.path.join(DIR_EXAMPLES,'Images')
         #path Novo Progresso site L7/L8/RE time series
         #pathTS = r'C:\Users\geo_beja\Repositories\QGIS_Plugins\SenseCarbonTSViewer\make\testdata_sources2.txt'
         pathTS = r'C:\Users\geo_beja\Repositories\QGIS_Plugins\SenseCarbonTSViewer\make\testdata_sources.txt'
@@ -406,6 +472,11 @@ if __name__ == '__main__':
 
         createTestData(pathDirTestData, pathTS,subset, crs, drv='ENVI')
         exit(0)
+    if True:
+
+        # update __init__.py of testdata directories
+        d = pathDirTestData = os.path.join(DIR_EXAMPLES,'Images')
+        createFilePackage(d)
 
     if False:
         createCreditsHTML()
diff --git a/timeseriesviewer/main.py b/timeseriesviewer/main.py
index 954ccf83e1dfd8dc6c7ab122815c86bbebe272b1..5a8747c52df0b667cc3cbbb4ab11999c5c248240 100644
--- a/timeseriesviewer/main.py
+++ b/timeseriesviewer/main.py
@@ -83,10 +83,18 @@ class SpatialExtent(QgsRectangle):
         crs = mapCanvas.mapSettings().destinationCrs()
         return SpatialExtent(crs, extent)
 
+    @staticmethod
+    def fromMapLayer(lyr):
+        assert isinstance(lyr, QgsMapLayer)
+        extent = lyr.extent()
+        crs = lyr.crs()
+        return SpatialExtent(crs, extent)
+
+
     def __init__(self, crs, *args):
-        assert isinstance(crs, QgsCoordinateReferenceSystem)
-        super(SpatialExtent, self).__init__(*args)
-        self.mCrs = crs
+            assert isinstance(crs, QgsCoordinateReferenceSystem)
+            super(SpatialExtent, self).__init__(*args)
+            self.mCrs = crs
 
     def setCrs(self, crs):
         assert isinstance(crs, QgsCoordinateReferenceSystem)
@@ -222,34 +230,89 @@ class QgisTsvBridge(QObject):
     def instance():
         return QgisTsvBridge._instance
 
-    def __init__(self, iface, TSV_UI):
+    def __init__(self, iface, TSV):
         super(QgisTsvBridge, self).__init__()
         assert QgisTsvBridge._instance is None
+        assert isinstance(TSV, TimeSeriesViewer)
         assert isinstance(iface, QgisInterface)
         self.iface = iface
-        self.ui = TSV_UI
+        self.TSV = TSV
+        self.ui = self.TSV.ui
+        self.SpatTempVis = self
+        self.syncBlocked = False
+
         from timeseriesviewer.ui.widgets import TimeSeriesViewerUI
         assert isinstance(self.ui, TimeSeriesViewerUI)
         self.cbQgsVectorLayer = self.ui.dockRendering.cbQgsVectorLayer
         self.gbQgsVectorLayer = self.ui.dockRendering.gbQgsVectorLayer
         self.qgsMapCanvas = self.iface.mapCanvas()
+        assert isinstance(self.qgsMapCanvas, QgsMapCanvas)
+
+        self.qgsMapCanvas.extentsChanged.connect(self.syncTsvWithQgs)
+        self.qgsMapCanvas.destinationCrsChanged.connect(self.syncTsvWithQgs)
+
 
         assert isinstance(self.cbQgsVectorLayer, QgsMapLayerComboBox)
         assert isinstance(self.gbQgsVectorLayer, QgsCollapsibleGroupBox)
-        assert isinstance(self.qgsMapCanvas, QgsMapCanvas)
-
-        self.cbSyncQgsMapCenter = self.ui.dockNavigation.cbSyncQgsMapCenter
-        self.cbSyncQgsMapExtent = self.ui.dockNavigation.cbSyncQgsMapExtent
-        self.cbSyncQgsCRS = self.ui.dockNavigation.cbSyncQgsCRS
 
-        for cb in [self.cbSyncQgsMapCenter, self.cbSyncQgsMapExtent, self.cbSyncQgsCRS]:
-            assert isinstance(cb, QCheckBox)
-            self.cbSyncQgsMapExtent.clicked.connect(lambda : self.onSpatialSyncChanged())
+        self.TSV.spatialTemporalVis.sigSpatialExtentChanged.connect(self.syncQgsWithTsv)
 
         self.cbQgsVectorLayer.layerChanged.connect(self.onQgsVectorLayerChanged)
-    def onSpatialSyncChanged(self):
-        dprint('QgisTsvBridge: spatial sync changed')
-        s = ""
+
+    def syncTsvWithQgs(self, *args):
+        if self.syncBlocked:
+            return
+        syncState = self.ui.dockNavigation.qgsSyncState()
+        if any(syncState.values()):
+            self.syncBlocked = True
+            print(('# QGIS -> TSV#', syncState))
+            self.syncBlocked = True
+            QTimer.singleShot(500, lambda: self.unblock())
+            tsvExt = self.TSV.spatialTemporalVis.extent
+            qgsExt = SpatialExtent.fromMapCanvas(self.qgsMapCanvas)
+            newExtent = self.newExtent(tsvExt, syncState, qgsExt)
+            self.TSV.spatialTemporalVis.setSpatialExtent(newExtent)
+            self.syncBlocked = False
+
+        pass
+
+
+    def syncQgsWithTsv(self, spatialExtent):
+
+        if self.syncBlocked:
+            return
+
+
+        syncState = self.ui.dockNavigation.qgsSyncState()
+        if any(syncState.values()):
+            print(('# TSV -> QGIS #', syncState))
+            self.syncBlocked = True
+            QTimer.singleShot(500, lambda: self.unblock())
+            tsvExt = self.TSV.spatialTemporalVis.extent
+            qgsExt = SpatialExtent.fromMapCanvas(self.qgsMapCanvas)
+            newExtent = self.newExtent(qgsExt, syncState, tsvExt)
+            self.qgsMapCanvas.setDestinationCrs(newExtent.crs())
+            self.qgsMapCanvas.setExtent(newExtent)
+            self.syncBlocked = False
+
+            #QTimer.singleShot(500, lambda : self.unblock())
+    def unblock(self):
+        self.syncBlocked = False
+
+    def newExtent(self, oldExtent, syncState, newExtent):
+
+        crs = newExtent.crs() if syncState['crs'] else oldExtent.crs()
+        extent = oldExtent
+        if syncState['extent']:
+            extent = newExtent.toCrs(crs)
+        elif syncState['center']:
+            import copy
+            extent = copy.copy(oldExtent)
+            extent.setCenter(newExtent.center(), newExtent.crs())
+
+        return extent
+
+
     def onQgsVectorLayerChanged(self, lyr):
         dprint('QgisTsvBridge: selected Qgs Vector layer changed')
         s = ""
@@ -386,7 +449,7 @@ class MapView(QObject):
 
 class TimeSeriesDatumView(QObject):
 
-    sigExtentsChanged = pyqtSignal(SpatialExtent)
+    sigSpatialExtentChanged = pyqtSignal(SpatialExtent)
     sigRenderProgress = pyqtSignal(int,int)
     sigLoadingStarted = pyqtSignal(MapView, TimeSeriesDatum)
     sigLoadingFinished = pyqtSignal(MapView, TimeSeriesDatum)
@@ -476,11 +539,11 @@ class TimeSeriesDatumView(QObject):
         canvas = TsvMapCanvas(self, mapView, parent=self.ui)
 
         canvas.setFixedSize(self.subsetSize)
-        canvas.extentsChanged.connect(lambda : self.sigExtentsChanged.emit(canvas.spatialExtent()))
+        canvas.extentsChanged.connect(lambda : self.sigSpatialExtentChanged.emit(canvas.spatialExtent()))
         canvas.renderStarting.connect(lambda : self.sigLoadingStarted.emit(mapView, self.TSD))
         canvas.mapCanvasRefreshed.connect(lambda: self.sigLoadingFinished.emit(mapView, self.TSD))
         canvas.sigShowProfiles.connect(mapView.sigShowProfiles.emit)
-
+        canvas.sigSpatialExtentChanged.connect(mapView.sigSpatialExtentChanged.emit)
 
         self.mapCanvases[mapView] = canvas
         self.L.insertWidget(self.wOffset + i, canvas)
@@ -505,6 +568,7 @@ class SpatialTemporalVisualization(QObject):
     sigLoadingFinished = pyqtSignal(TimeSeriesDatumView, MapView)
     sigShowProfiles = pyqtSignal(QgsPoint, QgsCoordinateReferenceSystem)
     sigShowMapLayerInfo = pyqtSignal(dict)
+    sigSpatialExtentChanged = pyqtSignal(SpatialExtent)
 
     def __init__(self, timeSeriesViewer):
         assert isinstance(timeSeriesViewer, TimeSeriesViewer)
@@ -518,10 +582,12 @@ class SpatialTemporalVisualization(QObject):
         self.dockMapViews = self.ui.dockMapViews
         self.MVC = MapViewCollection(self)
         self.MVC.sigShowProfiles.connect(self.sigShowProfiles.emit)
+
         self.timeSeriesDateViewCollection = TimeSeriesDateViewCollection(self)
         self.timeSeriesDateViewCollection.sigResizeRequired.connect(self.adjustScrollArea)
         self.timeSeriesDateViewCollection.sigLoadingStarted.connect(self.ui.dockRendering.addStartedWork)
         self.timeSeriesDateViewCollection.sigLoadingFinished.connect(self.ui.dockRendering.addFinishedWork)
+        self.timeSeriesDateViewCollection.sigSpatialExtentChanged.connect(self.onSpatialExtentChanged)
         self.TS.sigTimeSeriesDatesAdded.connect(self.timeSeriesDateViewCollection.addDates)
         self.TS.sigTimeSeriesDatesRemoved.connect(self.timeSeriesDateViewCollection.removeDates)
         #add dates, if already existing
@@ -578,10 +644,13 @@ class SpatialTemporalVisualization(QObject):
         #todo: remove views
 
     def setSpatialExtent(self, extent):
-        self.extent = extent
         if extent:
             self.timeSeriesDateViewCollection.setSpatialExtent(extent)
+            self.onSpatialExtentChanged(extent)
 
+    def onSpatialExtentChanged(self, extent):
+        self.extent = extent
+        self.sigSpatialExtentChanged.emit(extent)
 
     def navigateToTSD(self, TSD):
         assert isinstance(TSD, TimeSeriesDatum)
@@ -608,6 +677,7 @@ class TimeSeriesDateViewCollection(QObject):
     sigLoadingStarted = pyqtSignal(MapView, TimeSeriesDatum)
     sigLoadingFinished = pyqtSignal(MapView, TimeSeriesDatum)
     sigShowProfiles = pyqtSignal(QgsPoint, QgsCoordinateReferenceSystem)
+    sigSpatialExtentChanged = pyqtSignal(QgsRectangle)
 
     def __init__(self, STViz):
         assert isinstance(STViz, SpatialTemporalVisualization)
@@ -665,10 +735,17 @@ class TimeSeriesDateViewCollection(QObject):
     def setFocusView(self, tsd):
         self.focusView = tsd
 
+    def onSpatialExtentChanged(self, extent):
+        for tsdview in self.orderedViews():
+            tsdview.setSpatialExtent(extent)
+        self.sigSpatialExtentChanged.emit(extent)
+
     def setSpatialExtent(self, extent):
         for tsdview in self.orderedViews():
             tsdview.setSpatialExtent(extent)
 
+
+
     def orderedViews(self):
         #returns the
         if self.focusView is not None:
@@ -703,7 +780,7 @@ class TimeSeriesDateViewCollection(QObject):
             tsdView = TimeSeriesDatumView(tsd, self, self.STViz.MVC, parent=self.ui)
             tsdView.setSubsetSize(self.subsetSize)
 
-            tsdView.sigExtentsChanged.connect(self.setSpatialExtent)
+            tsdView.sigSpatialExtentChanged.connect(self.onSpatialExtentChanged)
             tsdView.sigLoadingStarted.connect(self.sigLoadingStarted.emit)
             tsdView.sigLoadingFinished.connect(self.sigLoadingFinished.emit)
             tsdView.sigVisibilityChanged.connect(lambda: self.STViz.adjustScrollArea())
@@ -857,9 +934,6 @@ class MapViewCollection(QObject):
             btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
 
 
-    def setSpatialExtent(self, extent):
-        for mv in self.mapViewsDefinitions:
-            mv.setSpatialExtent(extent)
 
     def showMapViewDefinition(self, mapViewDefinition):
         assert mapViewDefinition in self.mapViewsDefinitions
@@ -916,10 +990,6 @@ class TimeSeriesViewer:
         from timeseriesviewer.ui.widgets import TimeSeriesViewerUI
 
         self.ui = TimeSeriesViewerUI()
-        if iface:
-            import timeseriesviewer
-            timeseriesviewer.QGIS_TSV_BRIDGE = QgisTsvBridge(iface, self.ui)
-            self.ui.setQgsLinkWidgets()
 
         #init empty time series
         self.TS = TimeSeries()
@@ -948,7 +1018,7 @@ class TimeSeriesViewer:
         self.spatialTemporalVis.sigShowProfiles.connect(self.spectralTemporalVis.loadCoordinate)
 
         self.spectralTemporalVis.sigMoveToTSD.connect(self.spatialTemporalVis.navigateToTSD)
-        D.dockNavigation.sigSpatialExtentChanged.connect(self.spatialTemporalVis.setSpatialExtent)
+        D.dockNavigation.sigSetSpatialExtent.connect(self.spatialTemporalVis.setSpatialExtent)
         self.ValidatorPxX = QIntValidator(0,99999)
         self.ValidatorPxY = QIntValidator(0,99999)
 
@@ -994,6 +1064,11 @@ class TimeSeriesViewer:
 
         self.canvasCrs = QgsCoordinateReferenceSystem()
 
+        if iface:
+            import timeseriesviewer
+            timeseriesviewer.QGIS_TSV_BRIDGE = QgisTsvBridge(iface, self)
+            self.ui.setQgsLinkWidgets()
+
 
     def loadImageFiles(self, files):
         assert isinstance(files, list)
@@ -1223,6 +1298,8 @@ def showRGBData(data):
 
 def run_tests():
 
+
+
     if False:
 
         pathImg = r'O:\SenseCarbonProcessing\BJ_NOC\01_RasterData\00_VRTs\02_Cutted\2014-07-26_LC82270652014207LGN00_BOA.vrt'
diff --git a/timeseriesviewer/tests.py b/timeseriesviewer/tests.py
index 6c9b1d5d708d05fc70b01b03155717174b12bd7f..440093a1e3eb96cd9c20f5ce3dc81287a602039a 100644
--- a/timeseriesviewer/tests.py
+++ b/timeseriesviewer/tests.py
@@ -57,6 +57,94 @@ def test_gui():
         return
     pass
 
+class QgisFake(QgisInterface):
+
+    def __init__(self, *args):
+        super(QgisFake, self).__init__(*args)
+
+        self.canvas = QgsMapCanvas()
+        self.canvas.blockSignals(False)
+        print(self.canvas)
+        self.canvas.setCrsTransformEnabled(True)
+        self.canvas.setCanvasColor(Qt.black)
+        self.canvas.extentsChanged.connect(self.testSlot)
+        self.layerTreeView = QgsLayerTreeView()
+        self.rootNode =QgsLayerTreeGroup()
+        self.treeModel = QgsLayerTreeModel(self.rootNode)
+        self.layerTreeView.setModel(self.treeModel)
+        self.bridge = QgsLayerTreeMapCanvasBridge(self.rootNode, self.canvas)
+        self.bridge.setAutoSetupOnFirstLayer(True)
+        self.ui = QMainWindow()
+        mainFrame = QFrame()
+
+        self.ui.setCentralWidget(mainFrame)
+        self.ui.setWindowTitle('Fake QGIS')
+        l = QHBoxLayout()
+        l.addWidget(self.layerTreeView)
+        l.addWidget(self.canvas)
+        mainFrame.setLayout(l)
+        self.ui.setCentralWidget(mainFrame)
+        self.lyrs = []
+        self.createActions()
+
+    def testSlot(self, *args):
+        #print('--canvas changes--')
+        s = ""
+
+    def addVectorLayer(selfpath, basename, providerkey):
+        pass
+
+    def addRasterLayer(self, path, baseName=''):
+        l = QgsRasterLayer(path, loadDefaultStyleFlag=True)
+        self.lyrs.append(l)
+        QgsMapLayerRegistry.instance().addMapLayer(l, True)
+        self.rootNode.addLayer(l)
+        self.bridge.setCanvasLayers()
+        return
+
+        cnt = len(self.canvas.layers())
+
+        self.canvas.setLayerSet([QgsMapCanvasLayer(l)])
+        l.dataProvider()
+        if cnt == 0:
+            self.canvas.mapSettings().setDestinationCrs(l.crs())
+            self.canvas.setExtent(l.extent())
+            from timeseriesviewer.main import SpatialExtent
+
+            spatialExtent = SpatialExtent.fromMapLayer(l)
+            #self.canvas.blockSignals(True)
+            self.canvas.setDestinationCrs(spatialExtent.crs())
+            self.canvas.setExtent(spatialExtent)
+            #self.blockSignals(False)
+            self.canvas.refresh()
+
+        self.canvas.refresh()
+
+    def createActions(self):
+        m = self.ui.menuBar().addAction('Add Vector')
+        m = self.ui.menuBar().addAction('Add Raster')
+
+    def mapCanvas(self):
+        return self.canvas
+
+def test_qgisbridge():
+    from timeseriesviewer.main import TimeSeriesViewer
+    from timeseriesviewer import PATH_EXAMPLE_TIMESERIES
+
+    fakeQGIS = QgisFake()
+
+    S = TimeSeriesViewer(fakeQGIS)
+    S.ui.show()
+    S.run()
+
+    fakeQGIS.ui.show()
+    import example.Images
+    fakeQGIS.addRasterLayer(example.Images.Img_2014_08_03_LE72270652014215CUB00_BOA)
+    S.loadImageFiles([example.Images.Img_2014_01_15_LC82270652014015LGN00_BOA])
+    s = ""
+
+
+
 def test_component():
 
     pass
@@ -84,7 +172,8 @@ if __name__ == '__main__':
     qgsApp.initQgis()
 
     #run tests
-    if True: test_gui()
+    if True: test_qgisbridge()
+    if False: test_gui()
     if False: test_component()
 
 
diff --git a/timeseriesviewer/ui/docks.py b/timeseriesviewer/ui/docks.py
index 83daa54d0c8e2ecdaa6e097b4acf06e7ae3a4d2a..d689a3eb8d2e6f6a683a7578527be612ce67a54b 100644
--- a/timeseriesviewer/ui/docks.py
+++ b/timeseriesviewer/ui/docks.py
@@ -106,21 +106,32 @@ class NavigationDockUI(TsvDockWidgetBase, load('navigationdock.ui')):
 
         #default: disable QgsSync box
         self.gbSyncQgs.setEnabled(False)
-        self.btnCrs.crsChanged.connect(self.sigCrsChanged.emit)
+        self.btnCrs.crsChanged.connect(self.sigSetCrs.emit)
         self.btnCrs.crsChanged.connect(self.onCrsUpdated)
 
-        self.cbSyncQgsMapExtent.clicked.connect(self.qgsSyncStateChanged)
-        self.cbSyncQgsMapCenter.clicked.connect(self.qgsSyncStateChanged)
-        self.cbSyncQgsCRS.clicked.connect(self.qgsSyncStateChanged)
+        self.syncStateWidgets = [self.cbSyncQgsMapExtent, self.cbSyncQgsMapCenter, self.cbSyncQgsCRS]
 
         self.spatialExtentWidgets = [self.spinBoxExtentCenterX, self.spinBoxExtentCenterY,
                                      self.spinBoxExtentWidth, self.spinBoxExtentHeight]
 
+
         for sb in self.spatialExtentWidgets:
-            sb.valueChanged.connect(lambda: self.sigSpatialExtentChanged.emit(self.spatialExtent()))
+            sb.valueChanged.connect(self.onExtentDefinitionChanges)
+        for cb in self.syncStateWidgets:
+            cb.clicked.connect(self.onExtentDefinitionChanges)
+
 
         self.connectTimeSeries(None)
 
+    def onExtentDefinitionChanges(self):
+        if self.cbSyncQgsMapExtent.isChecked():
+            self.cbSyncQgsMapCenter.setEnabled(False)
+            self.cbSyncQgsMapCenter.setChecked(True)
+        else:
+            self.cbSyncQgsMapCenter.setEnabled(True)
+
+        self.sigSetSpatialExtent.emit(self.spatialExtent())
+
     def connectTimeSeries(self, TS):
         self.TS = TS
         self.timeSeriesInitialized = False
@@ -137,14 +148,17 @@ class NavigationDockUI(TsvDockWidgetBase, load('navigationdock.ui')):
             if not self.timeSeriesInitialized:
                 self.setSpatialExtent(self.TS.getMaxSpatialExtent(self.crs()))
                 self.timeSeriesInitialized = True
-                self.sigSpatialExtentChanged.emit(self.spatialExtent())
+                self.sigSetSpatialExtent.emit(self.spatialExtent())
 
 
-    def qgsSyncStateChanged(self, *args):
 
-        s = ""
+    def qgsSyncState(self):
+        return {'center':self.cbSyncQgsMapCenter.isChecked(),
+                'extent':self.cbSyncQgsMapExtent.isChecked(),
+                'crs':self.cbSyncQgsCRS.isChecked()}
+
 
-    sigCrsChanged = pyqtSignal(QgsCoordinateReferenceSystem)
+    sigSetCrs = pyqtSignal(QgsCoordinateReferenceSystem)
     def setCrs(self, crs):
         assert isinstance(crs, QgsCoordinateReferenceSystem)
         self.btnCrs.setCrs(crs)
@@ -157,8 +171,9 @@ class NavigationDockUI(TsvDockWidgetBase, load('navigationdock.ui')):
     def onCrsUpdated(self, crs):
         self.gbCrs.setTitle(crs.authid())
         self.textBoxCRSInfo.setPlainText(crs.toWkt())
+        self.sigSetCrs.emit(self.crs())
 
-    sigSpatialExtentChanged = pyqtSignal(SpatialExtent)
+    sigSetSpatialExtent = pyqtSignal(SpatialExtent)
 
     def spatialExtent(self):
         crs = self.crs()
@@ -192,7 +207,7 @@ class NavigationDockUI(TsvDockWidgetBase, load('navigationdock.ui')):
         self._blockSignals(states)
 
         if extent != old:
-            self.sigSpatialExtentChanged.emit(extent)
+            self.sigSetSpatialExtent.emit(extent)
 
     def _blockSignals(self, widgets, block=True):
         states = dict()
diff --git a/timeseriesviewer/ui/widgets.py b/timeseriesviewer/ui/widgets.py
index 3dd4258b8fd53f0570bd2e5f929b028fb6b90c31..2381f27627c36d5099777534d636e8cbe1d29240 100644
--- a/timeseriesviewer/ui/widgets.py
+++ b/timeseriesviewer/ui/widgets.py
@@ -43,10 +43,11 @@ class TsvScrollArea(QScrollArea):
 
 
 class TsvMapCanvas(QgsMapCanvas):
-
+    from timeseriesviewer.main import SpatialExtent
     saveFileDirectories = dict()
     #sigRendererChanged = pyqtSignal(QgsRasterRenderer)
     sigShowProfiles = pyqtSignal(QgsPoint, QgsCoordinateReferenceSystem)
+    sigSpatialExtentChanged = pyqtSignal(SpatialExtent)
 
     def __init__(self, tsdView, mapView, parent=None):
         super(TsvMapCanvas, self).__init__(parent=parent)
@@ -62,7 +63,8 @@ class TsvMapCanvas(QgsMapCanvas):
         self.setCanvasColor(SETTINGS.value('CANVAS_BACKGROUND_COLOR', QColor(0, 0, 0)))
         self.setContextMenuPolicy(Qt.DefaultContextMenu)
 
-        self.qgsInteraction = QgisTsvBridge.instance()
+        self.extentsChanged.connect(lambda : self.sigSpatialExtentChanged.emit(self.spatialExtent()))
+
 
         #self.scrollAreaContent = tsdView.TSDVC.STViz.targetLayout.parentWidget()
         #self.viewport = tsdView.TSDVC.STViz.targetLayout.parentWidget().parentWidget()