Index: applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/Demo.java
===================================================================
--- applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/Demo.java	(revision 29169)
+++ applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/Demo.java	(revision 29170)
@@ -63,4 +63,6 @@
         setExtendedState(JFrame.MAXIMIZED_BOTH);
         JPanel panel = new JPanel();
+        JPanel panelTop = new JPanel();
+        JPanel panelBottom = new JPanel();
         JPanel helpPanel = new JPanel();
 
@@ -73,4 +75,7 @@
         add(panel, BorderLayout.NORTH);
         add(helpPanel, BorderLayout.SOUTH);
+        panel.setLayout(new BorderLayout());
+        panel.add(panelTop, BorderLayout.NORTH);
+        panel.add(panelBottom, BorderLayout.SOUTH);
         JLabel helpLabel = new JLabel("Use right mouse button to move,\n "
                 + "left double click or mouse wheel to zoom.");
@@ -103,6 +108,6 @@
         });
         map.setTileLoader((TileLoader) tileLoaderSelector.getSelectedItem());
-        panel.add(tileSourceSelector);
-        panel.add(tileLoaderSelector);
+        panelTop.add(tileSourceSelector);
+        panelTop.add(tileLoaderSelector);
         final JCheckBox showMapMarker = new JCheckBox("Map markers visible");
         showMapMarker.setSelected(map.getMapMarkersVisible());
@@ -113,5 +118,5 @@
             }
         });
-        panel.add(showMapMarker);
+        panelBottom.add(showMapMarker);
         final JCheckBox showTileGrid = new JCheckBox("Tile grid visible");
         showTileGrid.setSelected(map.isTileGridVisible());
@@ -122,5 +127,5 @@
             }
         });
-        panel.add(showTileGrid);
+        panelBottom.add(showTileGrid);
         final JCheckBox showZoomControls = new JCheckBox("Show zoom controls");
         showZoomControls.setSelected(map.getZoomContolsVisible());
@@ -131,11 +136,18 @@
             }
         });
-        panel.add(showZoomControls);
-        panel.add(button);
+        panelBottom.add(showZoomControls);
+        final JCheckBox scrollWrapEnabled = new JCheckBox("Scrollwrap enabled");
+        scrollWrapEnabled.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                map.setScrollWrapEnabled(scrollWrapEnabled.isSelected());
+            }
+        });
+        panelBottom.add(scrollWrapEnabled);
+        panelBottom.add(button);
 
-        panel.add(zoomLabel);
-        panel.add(zoomValue);
-        panel.add(mperpLabelName);
-        panel.add(mperpLabelValue);
+        panelTop.add(zoomLabel);
+        panelTop.add(zoomValue);
+        panelTop.add(mperpLabelName);
+        panelTop.add(mperpLabelValue);
 
         add(map, BorderLayout.CENTER);
Index: applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/JMapViewer.java
===================================================================
--- applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/JMapViewer.java	(revision 29169)
+++ applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/JMapViewer.java	(revision 29170)
@@ -63,4 +63,5 @@
 
     protected boolean tileGridVisible;
+    protected boolean scrollWrapEnabled;
 
     protected TileController tileController;
@@ -510,4 +511,7 @@
         int x_max = getWidth();
         int y_max = getHeight();
+        
+        // calculate the length of the grid (number of squares per edge)
+        int gridLength = 1 << zoom;
 
         // paint the tiles in a spiral, starting from center of the map
@@ -523,5 +527,12 @@
                     if (x_min <= posx && posx <= x_max && y_min <= posy && posy <= y_max) {
                         // tile is visible
-                        Tile tile = tileController.getTile(tilex, tiley, zoom);
+                        Tile tile;
+                        if (scrollWrapEnabled) {
+                            // in case tilex is out of bounds, grab the tile to use for wrapping
+                            int tilexWrap = (((tilex % gridLength) + gridLength) % gridLength);
+                            tile = tileController.getTile(tilexWrap, tiley, zoom);
+                        } else {
+                            tile = tileController.getTile(tilex, tiley, zoom);
+                        }
                         if (tile != null) {
                             tile.paint(g, posx, posy);
@@ -543,8 +554,18 @@
         // outer border of the map
         int mapSize = tilesize << zoom;
-        g.drawRect(w2 - center.x, h2 - center.y, mapSize, mapSize);
+        if (scrollWrapEnabled) {
+            g.drawLine(0, h2 - center.y, getWidth(), h2 - center.y);
+            g.drawLine(0, h2 - center.y + mapSize, getWidth(), h2 - center.y + mapSize);
+        } else {
+            g.drawRect(w2 - center.x, h2 - center.y, mapSize, mapSize);
+        }
 
         // g.drawString("Tiles in cache: " + tileCache.getTileCount(), 50, 20);
 
+        // keep x-coordinates from growing without bound if scroll-wrap is enabled
+        if (scrollWrapEnabled) {
+            center.x = center.x % mapSize;
+        }
+        
         if (mapPolygonsVisible && mapPolygonList != null) {
             for (MapPolygon polygon : mapPolygonList) {
@@ -573,6 +594,27 @@
     protected void paintMarker(Graphics g, MapMarker marker) {
         Point p = getMapPosition(marker.getLat(), marker.getLon());
-        if (p != null) {
+        if (scrollWrapEnabled) {
+            int tilesize = tileSource.getTileSize();
+            int mapSize = tilesize << zoom;
+            if (p == null) {
+                p = getMapPosition(marker.getLat(), marker.getLon(), false);
+            }
             marker.paint(g, p);
+            int xSave = p.x;
+            int xWrap = xSave;
+            // overscan of 15 allows up to 30-pixel markers to gracefully scroll off the edge of the panel
+            while ((xWrap -= mapSize) >= -15) {
+                p.x = xWrap;
+                marker.paint(g, p);
+            }
+            xWrap = xSave;
+            while ((xWrap += mapSize) <= getWidth() + 15) {
+                p.x = xWrap;
+                marker.paint(g, p);
+            }
+        } else {
+            if (p != null) {
+                marker.paint(g, p);
+            }
         }
     }
@@ -589,4 +631,27 @@
             if (pTopLeft != null && pBottomRight != null) {
                 rectangle.paint(g, pTopLeft, pBottomRight);
+                if (scrollWrapEnabled) {
+                    int tilesize = tileSource.getTileSize();
+                    int mapSize = tilesize << zoom;
+                    int xTopLeftSave = pTopLeft.x;
+                    int xTopLeftWrap = xTopLeftSave;
+                    int xBottomRightSave = pBottomRight.x;
+                    int xBottomRightWrap = xBottomRightSave;
+                    while ((xBottomRightWrap -= mapSize) >= 0) {
+                        xTopLeftWrap -= mapSize;
+                        pTopLeft.x = xTopLeftWrap;
+                        pBottomRight.x = xBottomRightWrap;
+                        rectangle.paint(g, pTopLeft, pBottomRight);
+                    }
+                    xTopLeftWrap = xTopLeftSave;
+                    xBottomRightWrap = xBottomRightSave;
+                    while ((xTopLeftWrap += mapSize) <= getWidth()) {
+                        xBottomRightWrap += mapSize;
+                        pTopLeft.x = xTopLeftWrap;
+                        pBottomRight.x = xBottomRightWrap;
+                        rectangle.paint(g, pTopLeft, pBottomRight);
+                    }
+                    
+                }
             }
         }
@@ -608,4 +673,30 @@
             }
             polygon.paint(g, points);
+            if (scrollWrapEnabled) {
+                int tilesize = tileSource.getTileSize();
+                int mapSize = tilesize << zoom;
+                List<Point> pointsWrapped = new LinkedList<Point>(points);
+                boolean keepWrapping = true;
+                while (keepWrapping) {
+                    for (Point p : pointsWrapped) {
+                        p.x -= mapSize;
+                        if (p.x < 0) {
+                            keepWrapping = false;
+                        }
+                    }
+                    polygon.paint(g, pointsWrapped);
+                }
+                pointsWrapped = new LinkedList<Point>(points);
+                keepWrapping = true;
+                while (keepWrapping) {
+                    for (Point p : pointsWrapped) {
+                        p.x += mapSize;
+                        if (p.x > getWidth()) {
+                            keepWrapping = false;
+                        }
+                    }
+                    polygon.paint(g, pointsWrapped);
+                }
+            }
         }
     }
@@ -867,4 +958,13 @@
         repaint();
     }
+    
+    public boolean isScrollWrapEnabled() {
+        return scrollWrapEnabled;
+    }
+    
+    public void setScrollWrapEnabled(boolean scrollWrapEnabled) {
+        this.scrollWrapEnabled = scrollWrapEnabled;
+        repaint();
+    }
 
     /**
