Index: applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/Tile.java
===================================================================
--- applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/Tile.java	(revision 31121)
+++ applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/Tile.java	(revision 31122)
@@ -332,3 +332,11 @@
         loaded = true;
     }
+
+    /**
+     *
+     * @return TileSource from which this tile comes
+     */
+    public TileSource getTileSource() {
+        return source;
+    }
 }
Index: applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/interfaces/TileSource.java
===================================================================
--- applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/interfaces/TileSource.java	(revision 31121)
+++ applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/interfaces/TileSource.java	(revision 31122)
@@ -3,4 +3,6 @@
 
 import java.io.IOException;
+import java.util.List;
+import java.util.Map;
 
 import org.openstreetmap.gui.jmapviewer.JMapViewer;
@@ -156,3 +158,14 @@
      */
     double tileYToLat(int y, int zoom);
+
+    /**
+     * Determines, if the returned data from TileSource represent "no tile at this zoom level" situation. Detection
+     * algorithms differ per TileSource, so each TileSource should implement each own specific way.
+     *
+     * @param headers HTTP headers from response from TileSource server
+     * @param statusCode HTTP status code
+     * @param content byte array representing the data returned from the server
+     * @return true, if "no tile at this zoom level" situation detected
+     */
+    public boolean isNoTileAtZoom(Map<String, List<String>> headers, int statusCode, byte[] content);
 }
Index: applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractOsmTileSource.java
===================================================================
--- applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractOsmTileSource.java	(revision 31121)
+++ applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractOsmTileSource.java	(revision 31122)
@@ -7,5 +7,5 @@
 
 /**
- * Abstract clas for OSM Tile sources
+ * Abstract class for OSM Tile sources
  */
 public abstract class AbstractOsmTileSource extends AbstractTMSTileSource {
@@ -24,7 +24,9 @@
      */
     public AbstractOsmTileSource(String name, String base_url, String id) {
-        super(name, base_url, id);
+        super(new TileSourceInfo(name, base_url, id));
+
     }
 
+    @Override
     public int getMaxZoom() {
         return 19;
Index: applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTMSTileSource.java
===================================================================
--- applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTMSTileSource.java	(revision 31121)
+++ applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTMSTileSource.java	(revision 31122)
@@ -3,4 +3,7 @@
 
 import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 
 import org.openstreetmap.gui.jmapviewer.OsmMercator;
@@ -11,12 +14,14 @@
     protected String baseUrl;
     protected String id;
+    private Map<String, String> noTileHeaders;
 
-    public AbstractTMSTileSource(String name, String base_url, String id) {
-        this.name = name;
-        this.baseUrl = base_url;
+    public AbstractTMSTileSource(TileSourceInfo info) {
+        this.name = info.getName();
+        this.baseUrl = info.getUrl();
         if(baseUrl.endsWith("/")) {
             baseUrl = baseUrl.substring(0,baseUrl.length()-1);
         }
-        this.id = id;
+        this.id = info.getUrl();
+        this.noTileHeaders = info.getNoTileHeaders();
     }
 
@@ -123,3 +128,16 @@
         return OsmMercator.XToLon(x * OsmMercator.TILE_SIZE, zoom);
     }
+
+    @Override
+    public boolean isNoTileAtZoom(Map<String, List<String>> headers, int statusCode, byte[] content) {
+        if(noTileHeaders != null) {
+            for (Entry<String, String> searchEntry: noTileHeaders.entrySet()) {
+                List<String> headerVals = headers.get(searchEntry.getKey());
+                if (headerVals != null && headerVals.contains(searchEntry.getValue())) {
+                    return true;
+                }
+            }
+        }
+        return super.isNoTileAtZoom(headers, statusCode, content);
+    }
 }
Index: applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTileSource.java
===================================================================
--- applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTileSource.java	(revision 31121)
+++ applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTileSource.java	(revision 31122)
@@ -3,7 +3,9 @@
 
 import java.awt.Image;
+import java.util.List;
+import java.util.Map;
 
+import org.openstreetmap.gui.jmapviewer.Coordinate;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
-import org.openstreetmap.gui.jmapviewer.Coordinate;
 
 abstract public class AbstractTileSource implements TileSource {
@@ -75,3 +77,7 @@
     }
 
+    public boolean isNoTileAtZoom(Map<String, List<String>> headers, int statusCode, byte[] content) {
+        // default handler - when HTTP 404 is returned, then treat this situation as no tile at this zoom level
+        return statusCode == 404;
+    }
 }
Index: applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java
===================================================================
--- applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java	(revision 31121)
+++ applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java	(revision 31122)
@@ -53,12 +53,9 @@
      */
     public BingAerialTileSource() {
-        this("Bing");
-    }
-
-    /**
-     * Constructs a new {@code BingAerialTileSource}.
-     */
-    public BingAerialTileSource(String id) {
-        super("Bing Aerial Maps", "http://example.com/", id);
+        super(new TileSourceInfo("Bing", null, null));
+    }
+
+    public BingAerialTileSource(TileSourceInfo info) {
+        super(info);
     }
 
Index: applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/ScanexTileSource.java
===================================================================
--- applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/ScanexTileSource.java	(revision 31121)
+++ applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/ScanexTileSource.java	(revision 31122)
@@ -46,6 +46,7 @@
     private ScanexLayer Layer = ScanexLayer.IRS;
 
-    public ScanexTileSource(String name, String url, String id, int maxZoom) {
-        super(name, url, id, maxZoom);
+    public ScanexTileSource(TileSourceInfo info) {
+        super(info);
+        String url = info.getUrl();
 
         for (ScanexLayer layer : ScanexLayer.values()) {
@@ -78,4 +79,5 @@
     }
 
+    @Override
     public TileUpdate getTileUpdate() {
         return TileUpdate.IfNoneMatch;
Index: applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/TMSTileSource.java
===================================================================
--- applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/TMSTileSource.java	(revision 31121)
+++ applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/TMSTileSource.java	(revision 31122)
@@ -1,4 +1,5 @@
 // License: GPL. For details, see Readme.txt file.
 package org.openstreetmap.gui.jmapviewer.tilesources;
+
 
 public class TMSTileSource extends AbstractTMSTileSource {
@@ -7,13 +8,8 @@
     protected int minZoom = 0;
 
-    public TMSTileSource(String name, String url, String id, int maxZoom) {
-        super(name, url, id);
-        this.maxZoom = maxZoom;
-    }
-
-    public TMSTileSource(String name, String url, String id, int minZoom, int maxZoom) {
-        super(name, url, id);
-        this.minZoom = minZoom;
-        this.maxZoom = maxZoom;
+    public TMSTileSource(TileSourceInfo info) {
+        super(info);
+        minZoom = info.getMinZoom();
+        maxZoom = info.getMaxZoom();
     }
 
Index: applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/TemplatedTMSTileSource.java
===================================================================
--- applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/TemplatedTMSTileSource.java	(revision 31121)
+++ applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/TemplatedTMSTileSource.java	(revision 31122)
@@ -2,9 +2,9 @@
 package org.openstreetmap.gui.jmapviewer.tilesources;
 
+import java.util.HashMap;
 import java.util.Map;
-import java.util.HashMap;
 import java.util.Random;
+import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import java.util.regex.Matcher;
 
 public class TemplatedTMSTileSource extends TMSTileSource {
@@ -28,18 +28,8 @@
     };
 
-    public TemplatedTMSTileSource(String name, String url, String id, int maxZoom) {
-        super(name, url, id, maxZoom);
-        handleTemplate();
-    }
-
-    public TemplatedTMSTileSource(String name, String url, String id, int minZoom, int maxZoom) {
-        super(name, url, id, minZoom, maxZoom);
-        handleTemplate();
-    }
-
-    public TemplatedTMSTileSource(String name, String url, String id, int minZoom, int maxZoom, String cookies) {
-        super(name, url, id, minZoom, maxZoom);
-        if (cookies != null) {
-            headers.put(COOKIE_HEADER, cookies);
+    public TemplatedTMSTileSource(TileSourceInfo info) {
+        super(info);
+        if (info.getCookies() != null) {
+            headers.put(COOKIE_HEADER, info.getCookies());
         }
         handleTemplate();
Index: applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/TileSourceInfo.java
===================================================================
--- applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/TileSourceInfo.java	(revision 31122)
+++ applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/TileSourceInfo.java	(revision 31122)
@@ -0,0 +1,107 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.gui.jmapviewer.tilesources;
+
+import java.util.Map;
+
+/**
+ * Data class that keeps basic information about a tile source.
+ */
+public class TileSourceInfo {
+    /** id for this imagery entry, optional at the moment */
+    protected String id;
+    /** URL of the imagery service */
+    protected  String url = null;
+
+    /** name of the imagery layer */
+    protected String name;
+
+    /** headers meaning, that there is no tile at this zoom level */
+    protected Map<String, String> notileHeaders;
+
+    /** minimum zoom level supported by the tile source */
+    protected int minZoom;
+
+    /** maximum zoom level supported by the tile source */
+    protected int maxZoom;
+
+    /** cookies that needs to be sent to tile source */
+    protected String cookies;
+
+
+    /**
+     * Create a TileSourceInfo class
+     *
+     * @param name
+     * @param base_url
+     * @param id
+     */
+    public TileSourceInfo(String name, String base_url, String id) {
+        this(name);
+        this.url = base_url;
+        this.id = id;
+    }
+
+    /**
+     * Create a TileSourceInfo class
+     *
+     * @param name
+     */
+    public TileSourceInfo(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Creates empty TileSourceInfo class
+     */
+    public TileSourceInfo() {
+    }
+
+    /**
+     *
+     * @return name of the tile source
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     *
+     * @return url of the tile source
+     */
+    public String getUrl() {
+        return url;
+    }
+
+    /**
+     *
+     * @return map of headers, that when set, means that this is "no tile at this zoom level" situation
+     */
+    public Map<String, String> getNoTileHeaders() {
+        return notileHeaders;
+    }
+
+    /**
+     *
+     * @return minimum zoom level supported by tile source
+     */
+    public int getMinZoom() {
+        return minZoom;
+    }
+
+    /**
+     *
+     * @return maximum zoom level supported by tile source
+     */
+    public int getMaxZoom() {
+        return maxZoom;
+    }
+
+    /**
+     *
+     * @return cookies to be sent along with request to tile source
+     */
+    public String getCookies() {
+        return cookies;
+    }
+
+}
