Index: /trunk/src/org/openstreetmap/josm/data/Bounds.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/Bounds.java	(revision 4579)
+++ /trunk/src/org/openstreetmap/josm/data/Bounds.java	(revision 4580)
@@ -141,8 +141,8 @@
             throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' > 0.0 exptected, got {1}", "lonExtent", lonExtent));
 
-        this.minLat = LatLon.roundToOsmPrecision(center.lat() - latExtent / 2);
-        this.minLon = LatLon.roundToOsmPrecision(center.lon() - lonExtent / 2);
-        this.maxLat = LatLon.roundToOsmPrecision(center.lat() + latExtent / 2);
-        this.maxLon = LatLon.roundToOsmPrecision(center.lon() + lonExtent / 2);
+        this.minLat = LatLon.roundToOsmPrecision(LatLon.toIntervalLat(center.lat() - latExtent / 2));
+        this.minLon = LatLon.roundToOsmPrecision(LatLon.toIntervalLon(center.lon() - lonExtent / 2));
+        this.maxLat = LatLon.roundToOsmPrecision(LatLon.toIntervalLat(center.lat() + latExtent / 2));
+        this.maxLon = LatLon.roundToOsmPrecision(LatLon.toIntervalLon(center.lon() + lonExtent / 2));
     }
 
@@ -164,5 +164,13 @@
     public LatLon getCenter()
     {
-        return getMin().getCenter(getMax());
+        if (crosses180thMeridian()) {
+            LatLon result = new LatLon(minLat, minLon-360.0).getCenter(getMax());
+            if (result.lon() < -180.0) {
+                result.setLocation(result.lon()+360.0, result.lat());
+            }
+            return result;
+        } else {
+            return getMin().getCenter(getMax());
+        }
     }
 
@@ -174,12 +182,22 @@
             minLat = LatLon.roundToOsmPrecision(ll.lat());
         }
-        if (ll.lon() < minLon) {
-            minLon = LatLon.roundToOsmPrecision(ll.lon());
-        }
         if (ll.lat() > maxLat) {
             maxLat = LatLon.roundToOsmPrecision(ll.lat());
         }
-        if (ll.lon() > maxLon) {
-            maxLon = LatLon.roundToOsmPrecision(ll.lon());
+        if (crosses180thMeridian()) {
+            if (ll.lon() > maxLon && ll.lon() < minLon) {
+                if (Math.abs(ll.lon() - minLon) <= Math.abs(ll.lon() - maxLon)) {
+                    minLon = LatLon.roundToOsmPrecision(ll.lon());
+                } else {
+                    maxLon = LatLon.roundToOsmPrecision(ll.lon());
+                }
+            }
+        } else {
+            if (ll.lon() < minLon) {
+                minLon = LatLon.roundToOsmPrecision(ll.lon());
+            }
+            if (ll.lon() > maxLon) {
+                maxLon = LatLon.roundToOsmPrecision(ll.lon());
+            }
         }
     }
@@ -189,15 +207,25 @@
         extend(b.getMax());
     }
+    
     /**
      * Is the given point within this bounds?
      */
     public boolean contains(LatLon ll) {
-        if (ll.lat() < minLat || ll.lon() < minLon)
-            return false;
-        if (ll.lat() > maxLat || ll.lon() > maxLon)
-            return false;
+        if (ll.lat() < minLat || ll.lat() > maxLat)
+            return false;
+        if (crosses180thMeridian()) {
+            if (ll.lon() > maxLon && ll.lon() < minLon)
+                return false;
+        } else {
+            if (ll.lon() < minLon || ll.lon() > maxLon)
+                return false;
+        }
         return true;
     }
 
+    private static boolean intersectsLonCrossing(Bounds crossing, Bounds notCrossing) {
+        return notCrossing.minLon <= crossing.maxLon || notCrossing.maxLon >= crossing.minLon;
+    }
+    
     /**
      * The two bounds intersect? Compared to java Shape.intersects, if does not use
@@ -205,11 +233,27 @@
      */
     public boolean intersects(Bounds b) {
-        return b.maxLat >= minLat &&
-        b.maxLon >= minLon &&
-        b.minLat <= maxLat &&
-        b.minLon <= maxLon;
-    }
-
-
+        if (b.maxLat < minLat || b.minLat > maxLat)
+            return false;
+        
+        if (crosses180thMeridian() && !b.crosses180thMeridian()) {
+            return intersectsLonCrossing(this, b);
+        } else if (!crosses180thMeridian() && b.crosses180thMeridian()) {
+            return intersectsLonCrossing(b, this);
+        } else if (crosses180thMeridian() && b.crosses180thMeridian()) {
+            return true;
+        } else {
+            return b.maxLon >= minLon && b.minLon <= maxLon;
+        }
+    }
+
+    /**
+     * Determines if this Bounds object crosses the 180th Meridian.
+     * See http://wiki.openstreetmap.org/wiki/180th_meridian
+     * @return true if this Bounds object crosses the 180th Meridian.
+     */
+    public boolean crosses180thMeridian() {
+        return this.minLon > this.maxLon;
+    }
+    
     /**
      * Converts the lat/lon bounding box to an object of type Rectangle2D.Double
@@ -217,9 +261,11 @@
      */
     public Rectangle2D.Double asRect() {
-        return new Rectangle2D.Double(minLon, minLat, maxLon-minLon, maxLat-minLat);
+        double w = maxLon-minLon + (crosses180thMeridian() ? 360.0 : 0.0);
+        return new Rectangle2D.Double(minLon, minLat, w, maxLat-minLat);
     }
 
     public double getArea() {
-        return (maxLon - minLon) * (maxLat - minLat);
+        double w = maxLon-minLon + (crosses180thMeridian() ? 360.0 : 0.0);
+        return w * (maxLat - minLat);
     }
 
@@ -250,28 +296,9 @@
     }
 
-    private double toIntervalLat(double value, double min, double max) {
-        if (value < min)
-            return min;
-        if (value > max)
-            return max;
-        return value;
-    }
-
-    private double toIntervalLon(double value) {
-        if (value < -180 || value > 180) {
-            value = (value + 180) % 360;
-            if (value < 0) {
-                value += 360;
-            }
-            return value - 180;
-        } else
-            return value;
-    }
-
     public void normalize() {
-        minLat = toIntervalLat(minLat, -90, 90);
-        maxLat = toIntervalLat(maxLat, -90, 90);
-        minLon = toIntervalLon(minLon);
-        maxLon = toIntervalLon(maxLon);
+        minLat = LatLon.toIntervalLat(minLat);
+        maxLat = LatLon.toIntervalLat(maxLat);
+        minLon = LatLon.toIntervalLon(minLon);
+        maxLon = LatLon.toIntervalLon(maxLon);
     }
 
Index: /trunk/src/org/openstreetmap/josm/data/coor/LatLon.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/coor/LatLon.java	(revision 4579)
+++ /trunk/src/org/openstreetmap/josm/data/coor/LatLon.java	(revision 4580)
@@ -77,4 +77,26 @@
 		return isValidLat(lat()) && isValidLon(lon());
 	}
+	
+    public static double toIntervalLat(double value) {
+        if (value < -90)
+            return -90;
+        if (value > 90)
+            return 90;
+        return value;
+    }
+
+    /**
+     * Returns a valid OSM longitude [-180,+180] for the given extended longitude value.
+     * For example, a value of -181 will return +179, a value of +181 will return -179. 
+     * @param lon A longitude value not restricted to the [-180,+180] range.
+     */
+    public static double toIntervalLon(double value) {
+        if (isValidLon(value)) {
+            return value;
+        } else {
+            int n = (int) (value + Math.signum(value)*180.0) / 360;
+            return value - n*360.0;
+        }
+    }
  	
     /**
@@ -165,5 +187,5 @@
      */
     public boolean isWithin(Bounds b) {
-        return lat() >= b.getMin().lat() && lat() <= b.getMax().lat() && lon() > b.getMin().lon() && lon() < b.getMax().lon();
+        return b.contains(this);
     }
 
Index: /trunk/src/org/openstreetmap/josm/data/osm/DataSet.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 4579)
+++ /trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 4580)
@@ -1175,4 +1175,27 @@
         return ret;
     }
+    
+    /**
+     * Moves all primitives and datasources from DataSet "from" to this DataSet
+     * @param from The source DataSet
+     */
+    public void mergeFrom(DataSet from) {
+        if (from != null) {
+            for (Node n : from.getNodes()) {
+                from.removePrimitive(n);
+                addPrimitive(n);
+            }
+            for (Way w : from.getWays()) {
+                from.removePrimitive(w);
+                addPrimitive(w);
+            }
+            for (Relation r : from.getRelations()) {
+                from.removePrimitive(r);
+                addPrimitive(r);
+            }
+            dataSources.addAll(from.dataSources);
+            from.dataSources.clear();
+        }
+    }
 
     /* --------------------------------------------------------------------------------- */
Index: /trunk/src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java	(revision 4579)
+++ /trunk/src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java	(revision 4580)
@@ -311,14 +311,14 @@
         iSelectionRectEnd = pEnd;
 
-        Coordinate l1 = getPosition(p_max);
-        Coordinate l2 = getPosition(p_min);
+        Coordinate l1 = getPosition(p_max); // lon may be outside [-180,180]
+        Coordinate l2 = getPosition(p_min); // lon may be outside [-180,180]
         Bounds b = new Bounds(
                 new LatLon(
                         Math.min(l2.getLat(), l1.getLat()),
-                        Math.min(l1.getLon(), l2.getLon())
+                        LatLon.toIntervalLon(Math.min(l1.getLon(), l2.getLon()))
                 ),
                 new LatLon(
                         Math.max(l2.getLat(), l1.getLat()),
-                        Math.max(l1.getLon(), l2.getLon()))
+                        LatLon.toIntervalLon(Math.max(l1.getLon(), l2.getLon())))
         );
         Bounds oldValue = this.bbox;
@@ -364,9 +364,16 @@
 
         this.bbox = bbox;
+        double minLon = bbox.getMin().lon();
+        double maxLon = bbox.getMax().lon();
+        
+        if (bbox.crosses180thMeridian()) {
+            minLon -= 360.0;
+        }
+
         int y1 = OsmMercator.LatToY(bbox.getMin().lat(), MAX_ZOOM);
         int y2 = OsmMercator.LatToY(bbox.getMax().lat(), MAX_ZOOM);
-        int x1 = OsmMercator.LonToX(bbox.getMin().lon(), MAX_ZOOM);
-        int x2 = OsmMercator.LonToX(bbox.getMax().lon(), MAX_ZOOM);
-
+        int x1 = OsmMercator.LonToX(minLon, MAX_ZOOM);
+        int x2 = OsmMercator.LonToX(maxLon, MAX_ZOOM);
+        
         iSelectionRectStart = new Point(Math.min(x1, x2), Math.min(y1, y2));
         iSelectionRectEnd = new Point(Math.max(x1, x2), Math.max(y1, y2));
Index: /trunk/src/org/openstreetmap/josm/io/BoundingBoxDownloader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/BoundingBoxDownloader.java	(revision 4579)
+++ /trunk/src/org/openstreetmap/josm/io/BoundingBoxDownloader.java	(revision 4580)
@@ -22,4 +22,5 @@
     private final double lat2;
     private final double lon2;
+    private final boolean crosses180th;
 
     public BoundingBoxDownloader(Bounds downloadArea) {
@@ -28,6 +29,34 @@
         this.lat2 = downloadArea.getMax().lat();
         this.lon2 = downloadArea.getMax().lon();
+        this.crosses180th = downloadArea.crosses180thMeridian();
     }
 
+    private GpxData downloadRawGps(String url, ProgressMonitor progressMonitor) throws IOException, OsmTransferException, SAXException {
+        boolean done = false;
+        GpxData result = null;
+        for (int i = 0;!done;++i) {
+            progressMonitor.subTask(tr("Downloading points {0} to {1}...", i * 5000, ((i + 1) * 5000)));
+            InputStream in = getInputStream(url+i, progressMonitor.createSubTaskMonitor(1, true));
+            if (in == null) {
+                break;
+            }
+            progressMonitor.setTicks(0);
+            GpxReader reader = new GpxReader(in);
+            reader.parse(false);
+            GpxData currentGpx = reader.data;
+            if (result == null) {
+                result = currentGpx;
+            } else if (currentGpx.hasTrackPoints()) {
+                result.mergeFrom(currentGpx);
+            } else{
+                done = true;
+            }
+            in.close();
+            activeConnection = null;
+        }
+        result.fromServer = true;
+        return result;
+    }
+    
     /**
      * Retrieve raw gps waypoints from the server API.
@@ -41,30 +70,13 @@
         try {
             progressMonitor.indeterminateSubTask(tr("Contacting OSM Server..."));
-            String url = "trackpoints?bbox="+lon1+","+lat1+","+lon2+","+lat2+"&page=";
-
-            boolean done = false;
-            GpxData result = null;
-            for (int i = 0;!done;++i) {
-                progressMonitor.subTask(tr("Downloading points {0} to {1}...", i * 5000, ((i + 1) * 5000)));
-                InputStream in = getInputStream(url+i, progressMonitor.createSubTaskMonitor(1, true));
-                if (in == null) {
-                    break;
-                }
-                progressMonitor.setTicks(0);
-                GpxReader reader = new GpxReader(in);
-                reader.parse(false);
-                GpxData currentGpx = reader.data;
-                if (result == null) {
-                    result = currentGpx;
-                } else if (currentGpx.hasTrackPoints()) {
-                    result.mergeFrom(currentGpx);
-                } else{
-                    done = true;
-                }
-                in.close();
-                activeConnection = null;
+            if (crosses180th) {
+                // API 0.6 does not support requests crossing the 180th meridian, so make two requests
+                GpxData result = downloadRawGps("trackpoints?bbox="+lon1+","+lat1+",180.0,"+lat2+"&page=", progressMonitor);
+                result.mergeFrom(downloadRawGps("trackpoints?bbox=-180.0,"+lat1+","+lon2+","+lat2+"&page=", progressMonitor));
+                return result;
+            } else {
+                // Simple request
+                return downloadRawGps("trackpoints?bbox="+lon1+","+lat1+","+lon2+","+lat2+"&page=", progressMonitor);
             }
-            result.fromServer = true;
-            return result;
         } catch (IllegalArgumentException e) {
             // caused by HttpUrlConnection in case of illegal stuff in the response
@@ -98,9 +110,29 @@
         InputStream in = null;
         try {
+            DataSet ds = null;
             progressMonitor.indeterminateSubTask(null);
-            in = getInputStream("map?bbox="+lon1+","+lat1+","+lon2+","+lat2, progressMonitor.createSubTaskMonitor(9, false));
-            if (in == null)
-                return null;
-            return OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, false));
+            if (crosses180th) {
+                // API 0.6 does not support requests crossing the 180th meridian, so make two requests
+                in = getInputStream("map?bbox="+lon1+","+lat1+",180.0,"+lat2, progressMonitor.createSubTaskMonitor(9, false));
+                if (in == null)
+                    return null;
+                ds = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, false));
+
+                in = getInputStream("map?bbox=-180.0,"+lat1+","+lon2+","+lat2, progressMonitor.createSubTaskMonitor(9, false));
+                if (in == null)
+                    return null;
+                DataSet ds2 = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, false));
+                if (ds2 == null)
+                    return null;
+                ds.mergeFrom(ds2);
+                
+            } else {
+                // Simple request
+                in = getInputStream("map?bbox="+lon1+","+lat1+","+lon2+","+lat2, progressMonitor.createSubTaskMonitor(9, false));
+                if (in == null)
+                    return null;
+                ds = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, false));
+            }
+            return ds;
         } catch(OsmTransferException e) {
             throw e;
Index: /trunk/test/unit/org/openstreetmap/josm/data/BoundsTests.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/data/BoundsTests.java	(revision 4580)
+++ /trunk/test/unit/org/openstreetmap/josm/data/BoundsTests.java	(revision 4580)
@@ -0,0 +1,43 @@
+package org.openstreetmap.josm.data;
+
+import org.junit.Test;
+import org.openstreetmap.josm.data.coor.LatLon;
+
+import static org.junit.Assert.assertTrue;
+
+public class BoundsTests {
+
+    @Test
+    public void crossingTests() {
+        Bounds b1 = new Bounds(0, 170, 50, -170);
+        assertTrue(b1.crosses180thMeridian());
+        assertTrue(!b1.contains(new LatLon(-10, -180)));
+        assertTrue(b1.contains(new LatLon(0, -180)));
+        assertTrue(b1.contains(new LatLon(50, -180)));
+        assertTrue(!b1.contains(new LatLon(60, -180)));
+        assertTrue(!b1.contains(new LatLon(-10, 180)));
+        assertTrue(b1.contains(new LatLon(0, 180)));
+        assertTrue(b1.contains(new LatLon(50, 180)));
+        assertTrue(!b1.contains(new LatLon(60, 180)));
+
+        Bounds b2 = new Bounds(60, 170, 90, -170);
+        assertTrue(!b1.intersects(b2));
+        assertTrue(!b2.intersects(b1));
+
+        Bounds b3 = new Bounds(25, 170, 90, -170);
+        assertTrue(b1.intersects(b3));
+        assertTrue(b3.intersects(b1));
+        assertTrue(b2.intersects(b3));
+        assertTrue(b3.intersects(b2));
+        
+        b3.extend(b1);
+        assertTrue(b3.equals(new Bounds(0, 170, 90, -170)));
+        assertTrue(b1.intersects(b3));
+        assertTrue(b3.intersects(b1));
+        assertTrue(b2.intersects(b3));
+        assertTrue(b3.intersects(b2));
+        
+        b3.extend(new LatLon(0, 0));
+        assertTrue(b3.equals(new Bounds(0, 0, 90, -170)));
+    }
+}
Index: /trunk/test/unit/org/openstreetmap/josm/data/coor/LatLonTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/data/coor/LatLonTest.java	(revision 4579)
+++ /trunk/test/unit/org/openstreetmap/josm/data/coor/LatLonTest.java	(revision 4580)
@@ -73,3 +73,25 @@
         assertEquals(LatLon.roundToOsmPrecisionStrict(99.9999999),  99.9999999, 0);
     }
+    
+    @Test
+    public void toIntervalLonTests() {
+        assertEquals(-180.0, LatLon.toIntervalLon(-180.0), 0);
+        assertEquals(0.0, LatLon.toIntervalLon(0.0), 0);
+        assertEquals(180.0, LatLon.toIntervalLon(180.0), 0);
+
+        assertEquals(179.0, LatLon.toIntervalLon(-181.0), 0);
+        assertEquals(-179.0, LatLon.toIntervalLon(181.0), 0);
+
+        assertEquals(-1.0, LatLon.toIntervalLon(359.0), 0);
+        assertEquals(1.0, LatLon.toIntervalLon(-359.0), 0);
+
+        assertEquals(1.0, LatLon.toIntervalLon(361.0), 0);
+        assertEquals(-1.0, LatLon.toIntervalLon(-361.0), 0);
+
+        assertEquals(179.0, LatLon.toIntervalLon(539.0), 0);
+        assertEquals(-179.0, LatLon.toIntervalLon(-539.0), 0);
+
+        assertEquals(-179.0, LatLon.toIntervalLon(541.0), 0);
+        assertEquals(179.0, LatLon.toIntervalLon(-541.0), 0);
+    }
 }
