Index: /trunk/src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java	(revision 8617)
+++ /trunk/src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java	(revision 8618)
@@ -16,4 +16,5 @@
 
 import org.openstreetmap.gui.jmapviewer.Coordinate;
+import org.openstreetmap.gui.jmapviewer.OsmMercator;
 import org.openstreetmap.gui.jmapviewer.Tile;
 import org.openstreetmap.gui.jmapviewer.TileXY;
@@ -39,4 +40,5 @@
     private final List<String> serverProjections;
     private EastNorth topLeftCorner;
+    private Bounds worldBounds;
 
     private static final String PATTERN_HEADER  = "\\{header\\(([^,]+),([^}]+)\\)\\}";
@@ -79,7 +81,7 @@
      */
     public void initProjection(Projection proj) {
-        Bounds bounds = proj.getWorldBoundsLatLon();
-        EastNorth min = proj.latlon2eastNorth(bounds.getMin());
-        EastNorth max = proj.latlon2eastNorth(bounds.getMax());
+        this.worldBounds = getWorldBounds();
+        EastNorth min = proj.latlon2eastNorth(worldBounds.getMin());
+        EastNorth max = proj.latlon2eastNorth(worldBounds.getMax());
         this.topLeftCorner = new EastNorth(min.east(), max.north());
     }
@@ -230,10 +232,6 @@
     @Override
     public int getTileXMax(int zoom) {
-        Projection proj = Main.getProjection();
-        double scale = getDegreesPerTile(zoom);
-        Bounds bounds = Main.getProjection().getWorldBoundsLatLon();
-        EastNorth min = proj.latlon2eastNorth(bounds.getMin());
-        EastNorth max = proj.latlon2eastNorth(bounds.getMax());
-        return (int) Math.ceil(Math.abs(max.getX() - min.getX()) / scale);
+        LatLon bottomRight = new LatLon(worldBounds.getMinLat(), worldBounds.getMaxLon());
+        return latLonToTileXY(bottomRight.toCoordinate(), zoom).getXIndex();
     }
 
@@ -245,10 +243,6 @@
     @Override
     public int getTileYMax(int zoom) {
-        Projection proj = Main.getProjection();
-        double scale = getDegreesPerTile(zoom);
-        Bounds bounds = Main.getProjection().getWorldBoundsLatLon();
-        EastNorth min = proj.latlon2eastNorth(bounds.getMin());
-        EastNorth max = proj.latlon2eastNorth(bounds.getMax());
-        return (int) Math.ceil(Math.abs(max.getY() - min.getY()) / scale);
+        LatLon bottomRight = new LatLon(worldBounds.getMinLat(), worldBounds.getMaxLon());
+        return latLonToTileXY(bottomRight.toCoordinate(), zoom).getYIndex();
     }
 
@@ -354,11 +348,8 @@
 
     private double getDegreesPerTile(int zoom) {
-        return getDegreesPerTile(zoom, Main.getProjection());
-    }
-
-    private double getDegreesPerTile(int zoom, Projection proj) {
-        Bounds bounds = proj.getWorldBoundsLatLon();
-        EastNorth min = proj.latlon2eastNorth(bounds.getMin());
-        EastNorth max = proj.latlon2eastNorth(bounds.getMax());
+        Projection proj = Main.getProjection();
+        EastNorth min = proj.latlon2eastNorth(worldBounds.getMin());
+        EastNorth max = proj.latlon2eastNorth(worldBounds.getMax());
+
         int tilesPerZoom = (int) Math.pow(2, zoom - 1);
         return Math.max(
@@ -368,4 +359,24 @@
     }
 
+    /**
+     * returns world bounds, but detect situation, when default bounds are provided (-90, -180, 90, 180), and projection
+     * returns very close values for both min and max X. To work around this problem, cap this projection on north and south
+     * pole, the same way they are capped in Mercator projection, so conversions should work properly
+     */
+    private final static Bounds getWorldBounds() {
+        Projection proj = Main.getProjection();
+        Bounds bounds = proj.getWorldBoundsLatLon();
+        EastNorth min = proj.latlon2eastNorth(bounds.getMin());
+        EastNorth max = proj.latlon2eastNorth(bounds.getMax());
+
+        if (Math.abs(min.getX() - max.getX()) < 1 && bounds.equals(new Bounds(new LatLon(-90, -180), new LatLon(90, 180)))) {
+            return new Bounds(
+                    new LatLon(OsmMercator.MIN_LAT, bounds.getMinLon()),
+                    new LatLon(OsmMercator.MAX_LAT, bounds.getMaxLon())
+                    );
+        }
+        return bounds;
+    }
+
     @Override
     public String getTileId(int zoom, int tilex, int tiley) {
Index: /trunk/test/unit/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSourceTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSourceTest.java	(revision 8617)
+++ /trunk/test/unit/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSourceTest.java	(revision 8618)
@@ -15,4 +15,5 @@
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.BBox;
+import org.openstreetmap.josm.data.projection.CustomProjection;
 import org.openstreetmap.josm.data.projection.Projection;
 import org.openstreetmap.josm.data.projection.Projections;
@@ -67,4 +68,22 @@
 
     @Test
+    public void testEPSG4326_widebounds() {
+        Main.setProjection(new CustomProjection("+proj=lonlat +datum=WGS84 +axis=neu +bounds=-180,53,180,54"));
+        TemplatedWMSTileSource source = getSource();
+
+        verifyLocation(source, new LatLon(53.5937132, 19.5652017));
+        verifyLocation(source, new LatLon(53.501565692302854, 18.54455233898721));
+    }
+
+    @Test
+    public void testEPSG4326_narrowbounds() {
+        Main.setProjection(new CustomProjection("+proj=lonlat +datum=WGS84 +axis=neu +bounds=18,-90,20,90"));
+        TemplatedWMSTileSource source = getSource();
+
+        verifyLocation(source, new LatLon(53.5937132, 19.5652017));
+        verifyLocation(source, new LatLon(53.501565692302854, 18.54455233898721));
+    }
+
+    @Test
     public void testEPSG2180() {
         Main.setProjection(Projections.getProjectionByCode("EPSG:2180"));
@@ -77,4 +96,29 @@
         verifyTileSquarness(source, 150, 20, 18);
         verifyTileSquarness(source, 2270, 1323, 12);
+    }
+
+    @Test
+    public void testEPSG3006_withbounds() {
+        Main.setProjection(
+                new CustomProjection("+proj=utm +zone=33 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 "
+                        + "+units=m +no_defs +axis=neu +wmssrs=EPSG:3006 +bounds=10.5700,55.2000,24.1800,69.1000 "));
+        TemplatedWMSTileSource source = getSource();
+
+        verifyLocation(source, new LatLon(60, 18), 3);
+        verifyLocation(source, new LatLon(60, 18));
+
+    }
+
+    @Test
+    public void testEPSG3006_withoutbounds() {
+        Main.setProjection(
+                new CustomProjection("+proj=utm +zone=33 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 "
+                        + "+units=m +no_defs +axis=neu +wmssrs=EPSG:3006"));
+        TemplatedWMSTileSource source = getSource();
+
+        verifyTileSquarness(source, 0, 1, 4);
+        verifyLocation(source, new LatLon(60, 18.1), 3);
+        verifyLocation(source, new LatLon(60, 18.1));
+
     }
 
@@ -83,5 +127,4 @@
         LatLon result = getTileLatLon(source, x, y, z);
         LatLon expected = new LatLon(verifier.tileYToLat(y, z - 1), verifier.tileXToLon(x, z - 1)); //
-        System.out.println(z + "/" + x + "/" + y + " - result: " + result.toDisplayString() + " osmMercator: " +  expected.toDisplayString());
         assertTrue("result: " + result.toDisplayString() + " osmMercator: " +  expected.toDisplayString(), result.equalsEpsilon(expected));
         LatLon tileCenter = new Bounds(result, getTileLatLon(source, x+1, y+1, z)).getCenter();
@@ -92,14 +135,32 @@
 
     private void verifyLocation(TemplatedWMSTileSource source, LatLon location) {
-        for (int z = 1; z < 22; z++) {
-            TileXY tileIndex = source.latLonToTileXY(location.toCoordinate(), z);
-            BBox bbox = new BBox(
-                    getTileLatLon(source, tileIndex, z),
-                    getTileLatLon(source, tileIndex.getXIndex() + 1, tileIndex.getYIndex() + 1, z)
-                    );
-            assertTrue(location.toDisplayString() + " not within " + bbox.toString() +
-                    " for tile " + z + "/" + tileIndex.getXIndex() + "/" + tileIndex.getYIndex(),
-                    bbox.bounds(location));
+        for (int z = 22; z > 1; z--) {
+            verifyLocation(source, location, z);
         }
+    }
+
+    private void verifyLocation(TemplatedWMSTileSource source, LatLon location, int z) {
+        assertTrue(
+                "Point outside world bounds",
+                Main.getProjection().getWorldBoundsLatLon().contains(location)
+                );
+
+        TileXY tileIndex = source.latLonToTileXY(location.toCoordinate(), z);
+
+        assertTrue("X index: " + tileIndex.getXIndex() + " greater than tileXmax: " + source.getTileXMax(z) + " at zoom: " + z,
+                tileIndex.getXIndex() <= source.getTileXMax(z));
+
+        assertTrue("Y index: " + tileIndex.getYIndex() + " greater than tileYmax: " + source.getTileYMax(z) + " at zoom: " + z,
+                tileIndex.getYIndex() <= source.getTileYMax(z));
+
+        // test that location is within tile bounds
+        BBox bbox = new BBox(
+                getTileLatLon(source, tileIndex, z),
+                getTileLatLon(source, tileIndex.getXIndex() + 1, tileIndex.getYIndex() + 1, z)
+                );
+        assertTrue(location.toDisplayString() + " not within " + bbox.toString() +
+                " for tile " + z + "/" + tileIndex.getXIndex() + "/" + tileIndex.getYIndex(),
+                bbox.bounds(location));
+        verifyTileSquarness(source, tileIndex.getXIndex(), tileIndex.getYIndex(), z);
     }
 
@@ -119,14 +180,14 @@
          * t3 | t4
          */
-        EastNorth t1 = proj.latlon2eastNorth(getTileLatLon(source, x, y, z));
-        EastNorth t2 = proj.latlon2eastNorth(getTileLatLon(source, x + 1, y, z));
-        EastNorth t3 = proj.latlon2eastNorth(getTileLatLon(source, x, y + 1, z));
-        EastNorth t4 = proj.latlon2eastNorth(getTileLatLon(source, x + 1, y + 1, z));
+        EastNorth t1 = source.getTileEastNorth(x, y, z);
+        EastNorth t2 = source.getTileEastNorth(x + 1, y, z);
+        EastNorth t3 = source.getTileEastNorth(x, y + 1, z);
+        EastNorth t4 = source.getTileEastNorth(x + 1, y + 1, z);
         double y_size = Math.abs(t1.getY() - t4.getY());
         double x_size = Math.abs(t1.getX() - t4.getX());
 
-        assertEquals(x_size, y_size, Math.max(x_size, y_size) * 1e-05);
-        assertEquals(y_size, Math.abs(t1.getY() - t3.getY()), y_size * 1e-05);
-        assertEquals(x_size, Math.abs(t1.getX() - t2.getX()), x_size * 1e-05);
+        assertEquals(x_size, y_size, Math.max(x_size, y_size) * 1e-06);
+        assertEquals(y_size, Math.abs(t1.getY() - t3.getY()), y_size * 1e-06);
+        assertEquals(x_size, Math.abs(t1.getX() - t2.getX()), x_size * 1e-06);
 
         t1 = source.getTileEastNorth(x, y, z);
