Ticket #13287: patch-mapview-chunked-render-area.patch
File patch-mapview-chunked-render-area.patch, 29.1 KB (added by , 6 years ago) |
---|
-
src/org/openstreetmap/josm/data/projection/AbstractProjection.java
diff --git a/src/org/openstreetmap/josm/data/projection/AbstractProjection.java b/src/org/openstreetmap/josm/data/projection/AbstractProjection.java index c9a5ebf..8129cbc 100644
a b 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.projection; 3 3 4 import java.util.Collections; 5 import java.util.HashMap; 6 import java.util.Map; 7 import java.util.function.DoubleUnaryOperator; 8 4 9 import org.openstreetmap.josm.data.Bounds; 5 10 import org.openstreetmap.josm.data.ProjectionBounds; 6 11 import org.openstreetmap.josm.data.coor.EastNorth; 7 12 import org.openstreetmap.josm.data.coor.LatLon; 8 13 import org.openstreetmap.josm.data.projection.datum.Datum; 9 14 import org.openstreetmap.josm.data.projection.proj.Proj; 15 import org.openstreetmap.josm.tools.Utils; 10 16 11 17 /** 12 18 * Implementation of the Projection interface that represents a coordinate reference system and delegates … … public abstract class AbstractProjection implements Projection { 115 121 116 122 @Override 117 123 public LatLon eastNorth2latlon(EastNorth en) { 124 return eastNorth2latlon(en, LatLon::normalizeLon); 125 } 126 127 @Override 128 public LatLon eastNorth2latlonClamped(EastNorth en) { 129 LatLon ll = eastNorth2latlon(en, lon -> Utils.clamp(lon, -180, 180)); 130 Bounds bounds = getWorldBoundsLatLon(); 131 return new LatLon(Utils.clamp(ll.lat(), bounds.getMinLat(), bounds.getMaxLat()), 132 Utils.clamp(ll.lon(), bounds.getMinLon(), bounds.getMaxLon())); 133 } 134 135 private LatLon eastNorth2latlon(EastNorth en, DoubleUnaryOperator normalizeLon) { 118 136 double[] latlonRad = proj.invproject((en.east() * toMeter - x0) / ellps.a / k0, (en.north() * toMeter - y0) / ellps.a / k0); 119 LatLon ll = new LatLon(Math.toDegrees(latlonRad[0]), LatLon.normalizeLon(Math.toDegrees(latlonRad[1]) + lon0 + pm)); 137 double lon = Math.toDegrees(latlonRad[1]) + lon0 + pm; 138 LatLon ll = new LatLon(Math.toDegrees(latlonRad[0]), normalizeLon.applyAsDouble(lon)); 120 139 return datum.toWGS84(ll); 121 140 } 122 141 123 142 @Override 143 public Map<ProjectionBounds, Projecting> getProjectingsForArea(ProjectionBounds area) { 144 if (proj.lonIsLinearToEast()) { 145 //FIXME: Respect datum? 146 // wrap the wrold around 147 Bounds bounds = getWorldBoundsLatLon(); 148 double minEast = latlon2eastNorth(bounds.getMin()).east(); 149 double maxEast = latlon2eastNorth(bounds.getMax()).east(); 150 double dEast = maxEast - minEast; 151 if ((area.minEast < minEast || area.maxEast > maxEast) && dEast > 0) { 152 // We could handle the dEast < 0 case but we don't need it atm. 153 int minChunk = (int) Math.floor((area.minEast - minEast) / dEast); 154 int maxChunk = (int) Math.floor((area.maxEast - minEast) / dEast); 155 HashMap<ProjectionBounds, Projecting> ret = new HashMap<>(); 156 for (int chunk = minChunk; chunk <= maxChunk; chunk++) { 157 ret.put(new ProjectionBounds(Math.max(area.minEast, minEast + chunk * dEast), area.minNorth, 158 Math.min(area.maxEast, maxEast + chunk * dEast), area.maxNorth), 159 new ShiftedProjecting(this, new EastNorth(-chunk * dEast, 0))); 160 } 161 return ret; 162 } 163 } 164 165 return Collections.singletonMap(area, this); 166 } 167 168 @Override 124 169 public double getDefaultZoomInPPD() { 125 170 // this will set the map scaler to about 1000 m 126 171 return 10; … … public abstract class AbstractProjection implements Projection { 178 223 } 179 224 return projectionBoundsBox; 180 225 } 226 227 @Override 228 public Projection getBaseProjection() { 229 return this; 230 } 181 231 } -
new file src/org/openstreetmap/josm/data/projection/Projecting.java
diff --git a/src/org/openstreetmap/josm/data/projection/Projecting.java b/src/org/openstreetmap/josm/data/projection/Projecting.java new file mode 100644 index 0000000..e4e35cc
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.projection; 3 4 import java.util.Map; 5 6 import org.openstreetmap.josm.data.ProjectionBounds; 7 import org.openstreetmap.josm.data.coor.EastNorth; 8 import org.openstreetmap.josm.data.coor.LatLon; 9 10 /** 11 * Classes implementing this are able to project between screen (east/north) and {@link LatLon} coordinates. 12 * <p> 13 * Each instance is backed by a base projection but may e.g. offset the resulting position. 14 * @author Michael Zangl 15 * @since xxx 16 */ 17 public interface Projecting { 18 19 /** 20 * Convert from lat/lon to easting/northing. 21 * 22 * @param ll the geographical point to convert (in WGS84 lat/lon) 23 * @return the corresponding east/north coordinates 24 */ 25 EastNorth latlon2eastNorth(LatLon ll); 26 27 /** 28 * Convert a east/north coordinate to the {@link LatLon} coordinate. This method clamps the lat/lon coordinate to the nearest point in the world bounds. 29 * @param en east/north 30 * @return The lat/lon coordinate. 31 */ 32 LatLon eastNorth2latlonClamped(EastNorth en); 33 34 /** 35 * Gets the base projection instance used. 36 * @return The projection. 37 */ 38 Projection getBaseProjection(); 39 40 /** 41 * Returns an map or (subarea, projecting) paris that contains projecting instances to convert the coordinates inside the given area. 42 * This can be used by projections to support continuous projections. 43 * 44 * It is possible that the area covered by the map is bigger than the one given as area. There may be holes. 45 * @param area The base area 46 * @return a map of non-overlapping {@link ProjectionBounds} instances mapped to the {@link Projecting} object to use for that area. 47 */ 48 Map<ProjectionBounds, Projecting> getProjectingsForArea(ProjectionBounds area); 49 } -
src/org/openstreetmap/josm/data/projection/Projection.java
diff --git a/src/org/openstreetmap/josm/data/projection/Projection.java b/src/org/openstreetmap/josm/data/projection/Projection.java index b2bd016..7477210 100644
a b import org.openstreetmap.josm.data.coor.LatLon; 13 13 * The conversion from east/north to the screen coordinates is simply a scale 14 14 * factor and x/y offset. 15 15 */ 16 public interface Projection {16 public interface Projection extends Projecting { 17 17 /** 18 18 * The default scale factor in east/north units per pixel 19 19 * ({@link org.openstreetmap.josm.gui.NavigatableComponent#getState})). … … public interface Projection { 23 23 double getDefaultZoomInPPD(); 24 24 25 25 /** 26 * Convert from lat/lon to easting/northing.27 *28 * @param ll the geographical point to convert (in WGS84 lat/lon)29 * @return the corresponding east/north coordinates30 */31 EastNorth latlon2eastNorth(LatLon ll);32 33 /**34 26 * Convert from easting/norting to lat/lon. 35 27 * 36 28 * @param en the geographical point to convert (in projected coordinates) … … public interface Projection { 110 102 * @return true if natural order of coordinates is North East, false if East North 111 103 */ 112 104 boolean switchXY(); 105 113 106 } -
new file src/org/openstreetmap/josm/data/projection/ShiftedProjecting.java
diff --git a/src/org/openstreetmap/josm/data/projection/ShiftedProjecting.java b/src/org/openstreetmap/josm/data/projection/ShiftedProjecting.java new file mode 100644 index 0000000..0727c8a
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.projection; 3 4 import java.util.HashMap; 5 import java.util.Map; 6 7 import org.openstreetmap.josm.data.ProjectionBounds; 8 import org.openstreetmap.josm.data.coor.EastNorth; 9 import org.openstreetmap.josm.data.coor.LatLon; 10 11 /** 12 * This is a projecting instance that shifts the projection by a given eastnorth offset. 13 * @author Michael Zangl 14 * @since xxx 15 */ 16 public class ShiftedProjecting implements Projecting { 17 private final Projecting base; 18 private final EastNorth offset; 19 20 /** 21 * Create a new {@link ShiftedProjecting} 22 * @param base The base to use 23 * @param offset The offset to move base. Subtracted when converting lat/lon->east/north. 24 */ 25 public ShiftedProjecting(Projecting base, EastNorth offset) { 26 this.base = base; 27 this.offset = offset; 28 } 29 30 @Override 31 public EastNorth latlon2eastNorth(LatLon ll) { 32 return base.latlon2eastNorth(ll).add(offset); 33 } 34 35 @Override 36 public LatLon eastNorth2latlonClamped(EastNorth en) { 37 return base.eastNorth2latlonClamped(en.subtract(offset)); 38 } 39 40 @Override 41 public Projection getBaseProjection() { 42 return base.getBaseProjection(); 43 } 44 45 @Override 46 public Map<ProjectionBounds, Projecting> getProjectingsForArea(ProjectionBounds area) { 47 Map<ProjectionBounds, Projecting> forArea = base 48 .getProjectingsForArea(new ProjectionBounds(area.getMin().subtract(offset), area.getMax().subtract(offset))); 49 HashMap<ProjectionBounds, Projecting> ret = new HashMap<>(); 50 forArea.forEach((pb, projecting) -> ret.put( 51 new ProjectionBounds(pb.getMin().add(offset), pb.getMax().add(offset)), 52 new ShiftedProjecting(projecting, offset))); 53 return ret; 54 } 55 } -
src/org/openstreetmap/josm/data/projection/proj/Mercator.java
diff --git a/src/org/openstreetmap/josm/data/projection/proj/Mercator.java b/src/org/openstreetmap/josm/data/projection/proj/Mercator.java index 10d61ca..592d9ef 100644
a b public class Mercator extends AbstractProj implements IScaleFactorProvider { 119 119 public double getScaleFactor() { 120 120 return scaleFactor; 121 121 } 122 123 @Override 124 public boolean lonIsLinearToEast() { 125 return true; 126 } 122 127 } -
src/org/openstreetmap/josm/data/projection/proj/Proj.java
diff --git a/src/org/openstreetmap/josm/data/projection/proj/Proj.java b/src/org/openstreetmap/josm/data/projection/proj/Proj.java index fa9b3fe..b2bf5a5 100644
a b public interface Proj { 90 90 * @return true, if it is geographic 91 91 */ 92 92 boolean isGeographic(); 93 94 /** 95 * Checks wether the result of projecting a lon coordinate only has a linear relation to the east coordinate and 96 * is not related to lat/north at all. 97 * @return <code>true</code> if lon has a linear relationship to east only. 98 * @since xxx 99 */ 100 default boolean lonIsLinearToEast() { 101 return false; 102 } 93 103 } -
src/org/openstreetmap/josm/gui/MapViewState.java
diff --git a/src/org/openstreetmap/josm/gui/MapViewState.java b/src/org/openstreetmap/josm/gui/MapViewState.java index f7dcd8d..cd7b292 100644
a b import org.openstreetmap.josm.data.Bounds; 16 16 import org.openstreetmap.josm.data.ProjectionBounds; 17 17 import org.openstreetmap.josm.data.coor.EastNorth; 18 18 import org.openstreetmap.josm.data.coor.LatLon; 19 import org.openstreetmap.josm.data.projection.Projecting; 19 20 import org.openstreetmap.josm.data.projection.Projection; 20 21 import org.openstreetmap.josm.gui.download.DownloadDialog; 21 22 import org.openstreetmap.josm.tools.bugreport.BugReport; … … import org.openstreetmap.josm.tools.bugreport.BugReport; 27 28 */ 28 29 public final class MapViewState { 29 30 30 private final Projecti on projection;31 private final Projecting projecting; 31 32 32 33 private final int viewWidth; 33 34 private final int viewHeight; … … public final class MapViewState { 50 51 * @param scale The scale to use 51 52 * @param topLeft The top left corner in east/north space. 52 53 */ 53 private MapViewState(Projecti onprojection, int viewWidth, int viewHeight, double scale, EastNorth topLeft) {54 this.projecti on= projection;54 private MapViewState(Projecting projection, int viewWidth, int viewHeight, double scale, EastNorth topLeft) { 55 this.projecting = projection; 55 56 this.scale = scale; 56 57 this.topLeft = topLeft; 57 58 … … public final class MapViewState { 62 63 } 63 64 64 65 private MapViewState(EastNorth topLeft, MapViewState mapViewState) { 65 this.projecti on = mapViewState.projection;66 this.projecting = mapViewState.projecting; 66 67 this.scale = mapViewState.scale; 67 68 this.topLeft = topLeft; 68 69 … … public final class MapViewState { 73 74 } 74 75 75 76 private MapViewState(double scale, MapViewState mapViewState) { 76 this.projecti on = mapViewState.projection;77 this.projecting = mapViewState.projecting; 77 78 this.scale = scale; 78 79 this.topLeft = mapViewState.topLeft; 79 80 … … public final class MapViewState { 84 85 } 85 86 86 87 private MapViewState(JComponent position, MapViewState mapViewState) { 87 this.projecti on = mapViewState.projection;88 this.projecting = mapViewState.projecting; 88 89 this.scale = mapViewState.scale; 89 90 this.topLeft = mapViewState.topLeft; 90 91 … … public final class MapViewState { 105 106 } 106 107 } 107 108 108 private MapViewState(Projecti on projection, MapViewState mapViewState) {109 this.projecti on = projection;109 private MapViewState(Projecting projecting, MapViewState mapViewState) { 110 this.projecting = projecting; 110 111 this.scale = mapViewState.scale; 111 112 this.topLeft = mapViewState.topLeft; 112 113 … … public final class MapViewState { 200 201 * @return The projection. 201 202 */ 202 203 public Projection getProjection() { 203 return projecti on;204 return projecting.getBaseProjection(); 204 205 } 205 206 206 207 /** … … public final class MapViewState { 268 269 * @since 10486 269 270 */ 270 271 public MapViewState usingProjection(Projection projection) { 271 if (projection.equals(this.projecti on)) {272 if (projection.equals(this.projecting)) { 272 273 return this; 273 274 } else { 274 275 return new MapViewState(projection, this); … … public final class MapViewState { 357 358 /** 358 359 * Gets the current position in LatLon coordinates according to the current projection. 359 360 * @return The positon as LatLon. 361 * @see #getLatLonClamped() 360 362 */ 361 363 public LatLon getLatLon() { 362 return projection.eastNorth2latlon(getEastNorth()); 364 return projecting.getBaseProjection().eastNorth2latlon(getEastNorth()); 365 } 366 367 /** 368 * Gets the latlon coordinate clamped to the current world area. 369 * @return The lat/lon coordinate 370 * @since xxx 371 */ 372 public LatLon getLatLonClamped() { 373 return projecting.eastNorth2latlonClamped(getEastNorth()); 363 374 } 364 375 365 376 /** … … public final class MapViewState { 473 484 * @since 10458 474 485 */ 475 486 public Bounds getLatLonBoundsBox() { 476 return projection.getLatLonBoundsBox(getProjectionBounds()); 487 // TODO @michael2402: Use hillclimb. 488 return projecting.getBaseProjection().getLatLonBoundsBox(getProjectionBounds()); 477 489 } 478 490 479 491 /** -
src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
diff --git a/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java b/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java index 52dcbe9..2179312 100644
a b implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 230 230 } 231 231 232 232 protected void initTileSource(T tileSource) { 233 coordinateConverter = new TileCoordinateConverter(Main.map.mapView, getDisplaySettings());233 coordinateConverter = new TileCoordinateConverter(Main.map.mapView, tileSource, getDisplaySettings()); 234 234 attribution.initialize(tileSource); 235 235 236 236 currentZoomLevel = getBestZoom(); … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 367 367 * @return average number of screen pixels per tile pixel 368 368 */ 369 369 private double getScaleFactor(int zoom) { 370 if (!Main.isDisplayingMapView()) return 1; 371 MapView mv = Main.map.mapView; 372 LatLon topLeft = mv.getLatLon(0, 0); 373 LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight()); 374 TileXY t1 = tileSource.latLonToTileXY(topLeft.toCoordinate(), zoom); 375 TileXY t2 = tileSource.latLonToTileXY(botRight.toCoordinate(), zoom); 376 377 int screenPixels = mv.getWidth()*mv.getHeight(); 378 double tilePixels = Math.abs((t2.getY()-t1.getY())*(t2.getX()-t1.getX())*tileSource.getTileSize()*tileSource.getTileSize()); 379 if (screenPixels == 0 || tilePixels == 0) return 1; 380 return screenPixels/tilePixels; 370 if (coordinateConverter != null) { 371 return coordinateConverter.getScaleFactor(zoom); 372 } else { 373 return 1; 374 } 381 375 } 382 376 383 377 protected int getBestZoom() { … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1245 1239 } 1246 1240 1247 1241 private LatLon getShiftedLatLon(EastNorth en) { 1248 return Main.getProjection().eastNorth2latlon(en.add(-getDisplaySettings().getDx(), -getDisplaySettings().getDy()));1242 return coordinateConverter.getProjecting().eastNorth2latlonClamped(en); 1249 1243 } 1250 1244 1251 1245 private ICoordinate getShiftedCoord(EastNorth en) { … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1516 1510 1517 1511 @Override 1518 1512 public void paint(Graphics2D g, MapView mv, Bounds bounds) { 1519 ProjectionBounds pb = mv.getState().getViewArea().getProjectionBounds(); 1520 1521 needRedraw = false; 1513 // old and unused. 1514 } 1522 1515 1516 private void drawInViewArea(Graphics2D g, MapView mv, ProjectionBounds pb) { 1523 1517 int zoom = currentZoomLevel; 1524 1518 if (getDisplaySettings().isAutoZoom()) { 1525 1519 zoom = getBestZoom(); … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1894 1888 public void paint(MapViewGraphics graphics) { 1895 1889 allocateCacheMemory(); 1896 1890 if (memory != null) { 1897 super.paint(graphics);1891 doPaint(graphics); 1898 1892 } 1899 1893 } 1900 1894 1895 private void doPaint(MapViewGraphics graphics) { 1896 ProjectionBounds pb = graphics.getClipBounds().getProjectionBounds(); 1897 1898 needRedraw = false; // TEMPORARY 1899 1900 drawInViewArea(graphics.getDefaultGraphics(), graphics.getMapView(), pb); 1901 } 1902 1901 1903 private void allocateCacheMemory() { 1902 1904 if (memory == null) { 1903 1905 MemoryManager manager = MemoryManager.getInstance(); -
src/org/openstreetmap/josm/gui/layer/imagery/TileCoordinateConverter.java
diff --git a/src/org/openstreetmap/josm/gui/layer/imagery/TileCoordinateConverter.java b/src/org/openstreetmap/josm/gui/layer/imagery/TileCoordinateConverter.java index e8c925d..1489ff3 100644
a b import java.awt.geom.Point2D; 5 5 import java.awt.geom.Rectangle2D; 6 6 7 7 import org.openstreetmap.gui.jmapviewer.Tile; 8 import org.openstreetmap.gui.jmapviewer.TileXY; 8 9 import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate; 10 import org.openstreetmap.gui.jmapviewer.interfaces.TileSource; 9 11 import org.openstreetmap.josm.data.coor.LatLon; 12 import org.openstreetmap.josm.data.projection.Projecting; 13 import org.openstreetmap.josm.data.projection.ShiftedProjecting; 10 14 import org.openstreetmap.josm.gui.MapView; 11 15 import org.openstreetmap.josm.gui.MapViewState.MapViewPoint; 12 16 … … import org.openstreetmap.josm.gui.MapViewState.MapViewPoint; 18 22 public class TileCoordinateConverter { 19 23 private MapView mapView; 20 24 private TileSourceDisplaySettings settings; 25 private TileSource tileSource; 21 26 22 27 /** 23 28 * Create a new coordinate converter for the map view. 24 29 * @param mapView The map view. 30 * @param tileSource The tile source to use when converting coordinates. 25 31 * @param settings displacement settings. 26 32 */ 27 public TileCoordinateConverter(MapView mapView, TileSource DisplaySettings settings) {33 public TileCoordinateConverter(MapView mapView, TileSource tileSource, TileSourceDisplaySettings settings) { 28 34 this.mapView = mapView; 35 this.tileSource = tileSource; 29 36 this.settings = settings; 30 37 } 31 38 … … public class TileCoordinateConverter { 34 41 } 35 42 36 43 /** 44 * Gets the projecting instance to use to convert between latlon and eastnorth coordinates. 45 * @return The {@link Projecting} instance. 46 */ 47 public Projecting getProjecting() { 48 return new ShiftedProjecting(mapView.getProjection(), settings.getDisplacement()); 49 } 50 51 /** 37 52 * Gets the top left position of the tile inside the map view. 38 53 * @param tile The tile 39 54 * @return The positon. … … public class TileCoordinateConverter { 54 69 55 70 return pos(c1).rectTo(pos(c2)).getInView(); 56 71 } 72 73 /** 74 * Returns average number of screen pixels per tile pixel for current mapview 75 * @param zoom zoom level 76 * @return average number of screen pixels per tile pixel 77 */ 78 public double getScaleFactor(int zoom) { 79 LatLon topLeft = mapView.getLatLon(0, 0); 80 LatLon botRight = mapView.getLatLon(mapView.getWidth(), mapView.getHeight()); 81 TileXY t1 = tileSource.latLonToTileXY(topLeft.toCoordinate(), zoom); 82 TileXY t2 = tileSource.latLonToTileXY(botRight.toCoordinate(), zoom); 83 84 int screenPixels = mapView.getWidth()*mapView.getHeight(); 85 double tilePixels = Math.abs((t2.getY()-t1.getY())*(t2.getX()-t1.getX())*tileSource.getTileSize()*tileSource.getTileSize()); 86 if (screenPixels == 0 || tilePixels == 0) return 1; 87 return screenPixels/tilePixels; 88 } 57 89 } -
src/org/openstreetmap/josm/tools/Utils.java
diff --git a/src/org/openstreetmap/josm/tools/Utils.java b/src/org/openstreetmap/josm/tools/Utils.java index 0b92733..84968ba 100644
a b public final class Utils { 1549 1549 }); 1550 1550 } 1551 1551 } 1552 1553 /** 1554 * Clamp a value to the given range 1555 * @param val The value 1556 * @param min minimum value 1557 * @param max maximum value 1558 * @return the value 1559 */ 1560 public static double clamp(double val, double min, double max) { 1561 if (val < min) { 1562 return min; 1563 } else if (val > max) { 1564 return max; 1565 } else { 1566 return val; 1567 } 1568 } 1552 1569 } -
new file test/unit/org/openstreetmap/josm/data/projection/ShiftedProjectionTest.java
diff --git a/test/unit/org/openstreetmap/josm/data/projection/ShiftedProjectionTest.java b/test/unit/org/openstreetmap/josm/data/projection/ShiftedProjectionTest.java new file mode 100644 index 0000000..7748a44
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.projection; 3 4 import static org.junit.Assert.assertEquals; 5 6 import java.util.Comparator; 7 import java.util.HashMap; 8 import java.util.List; 9 import java.util.Map; 10 import java.util.Map.Entry; 11 import java.util.stream.Collectors; 12 13 import org.junit.Test; 14 import org.openstreetmap.josm.data.ProjectionBounds; 15 import org.openstreetmap.josm.data.coor.EastNorth; 16 import org.openstreetmap.josm.data.coor.LatLon; 17 18 /** 19 * Tests for {@link ShiftedProjecting} 20 * @author Michael Zangl 21 * @since xxx 22 */ 23 public class ShiftedProjectionTest { 24 private static final class ProjectingBase implements Projecting { 25 @Override 26 public EastNorth latlon2eastNorth(LatLon ll) { 27 return new EastNorth(ll.lat() * 2, ll.lon() * 3); 28 } 29 30 @Override 31 public Map<ProjectionBounds, Projecting> getProjectingsForArea(ProjectionBounds area) { 32 HashMap<ProjectionBounds, Projecting> map = new HashMap<>(); 33 // split at east = 0 34 if (area.minEast < 0) { 35 map.put(new ProjectionBounds(area.minEast, area.minNorth, Math.min(area.maxEast, 0), area.maxNorth), this); 36 } 37 if (area.maxEast > 0) { 38 map.put(new ProjectionBounds(Math.max(area.minEast, 0), area.minNorth, area.maxEast, area.maxNorth), this); 39 } 40 41 return map; 42 } 43 44 @Override 45 public Projection getBaseProjection() { 46 throw new AssertionError(); 47 } 48 49 @Override 50 public LatLon eastNorth2latlonClamped(EastNorth en) { 51 return new LatLon(en.east() / 2, en.north() / 3); 52 } 53 } 54 55 /** 56 * Test {@link ShiftedProjecting#latlon2eastNorth(LatLon)} 57 */ 58 @Test 59 public void testLatlon2eastNorth() { 60 Projecting base = new ProjectingBase(); 61 62 ShiftedProjecting unshifted = new ShiftedProjecting(base, new EastNorth(0, 0)); 63 EastNorth unshift_00 = unshifted.latlon2eastNorth(new LatLon(0, 0)); 64 assertEquals(0, unshift_00.east(), 1e-10); 65 assertEquals(0, unshift_00.north(), 1e-10); 66 EastNorth unshift_12 = unshifted.latlon2eastNorth(new LatLon(1, 2)); 67 assertEquals(2, unshift_12.east(), 1e-10); 68 assertEquals(6, unshift_12.north(), 1e-10); 69 70 ShiftedProjecting shifted = new ShiftedProjecting(base, new EastNorth(5, 7)); 71 EastNorth shift_00 = shifted.latlon2eastNorth(new LatLon(0, 0)); 72 assertEquals(5, shift_00.east(), 1e-10); 73 assertEquals(7, shift_00.north(), 1e-10); 74 EastNorth shift_12 = shifted.latlon2eastNorth(new LatLon(1, 2)); 75 assertEquals(2 + 5, shift_12.east(), 1e-10); 76 assertEquals(6 + 7, shift_12.north(), 1e-10); 77 } 78 79 /** 80 * Test {@link ShiftedProjecting#eastNorth2latlonClamped(EastNorth)} 81 */ 82 @Test 83 public void testEastNorth2latlonClamped() { 84 Projecting base = new ProjectingBase(); 85 86 ShiftedProjecting unshifted = new ShiftedProjecting(base, new EastNorth(0, 0)); 87 LatLon unshift_00 = unshifted.eastNorth2latlonClamped(new EastNorth(0, 0)); 88 assertEquals(0, unshift_00.lat(), 1e-10); 89 assertEquals(0, unshift_00.lon(), 1e-10); 90 LatLon unshift_12 = unshifted.eastNorth2latlonClamped(new EastNorth(2, 6)); 91 assertEquals(1, unshift_12.lat(), 1e-10); 92 assertEquals(2, unshift_12.lon(), 1e-10); 93 94 ShiftedProjecting shifted = new ShiftedProjecting(base, new EastNorth(5, 7)); 95 LatLon shift_00 = shifted.eastNorth2latlonClamped(new EastNorth(5, 7)); 96 assertEquals(0, shift_00.lat(), 1e-10); 97 assertEquals(0, shift_00.lon(), 1e-10); 98 LatLon shift_12 = shifted.eastNorth2latlonClamped(new EastNorth(2 + 5, 6 + 7)); 99 assertEquals(1, shift_12.lat(), 1e-10); 100 assertEquals(2, shift_12.lon(), 1e-10); 101 } 102 103 /** 104 * Test {@link ShiftedProjecting#getProjectingsForArea(ProjectionBounds)}, single area case 105 */ 106 @Test 107 public void testGetProjectingsForArea() { 108 Projecting base = new ProjectingBase(); 109 ShiftedProjecting shifted = new ShiftedProjecting(base, new EastNorth(5, 7)); 110 111 ProjectionBounds area = new ProjectionBounds(10, 0, 20, 20); 112 113 Map<ProjectionBounds, Projecting> areas = shifted.getProjectingsForArea(area); 114 assertEquals(1, areas.size()); 115 ProjectionBounds pb = areas.keySet().iterator().next(); 116 assertEquals(area.minEast, pb.minEast, 1e-7); 117 assertEquals(area.maxEast, pb.maxEast, 1e-7); 118 assertEquals(area.minNorth, pb.minNorth, 1e-7); 119 assertEquals(area.maxNorth, pb.maxNorth, 1e-7); 120 } 121 122 /** 123 * Test {@link ShiftedProjecting#getProjectingsForArea(ProjectionBounds)}, multiple area case 124 */ 125 @Test 126 public void testGetProjectingsForAreaMultiple() { 127 Projecting base = new ProjectingBase(); 128 ShiftedProjecting shifted = new ShiftedProjecting(base, new EastNorth(5, 7)); 129 130 ProjectionBounds area = new ProjectionBounds(-10, 0, 20, 20); 131 132 // breach is at: 133 EastNorth breachAt = shifted.latlon2eastNorth(base.eastNorth2latlonClamped(new EastNorth(0, 0))); 134 assertEquals(5, breachAt.east(), 1e-7); 135 136 Map<ProjectionBounds, Projecting> areas = shifted.getProjectingsForArea(area); 137 assertEquals(2, areas.size()); 138 List<Entry<ProjectionBounds, Projecting>> entries = areas.entrySet().stream().sorted(Comparator.comparingDouble(b -> b.getKey().minEast)).collect(Collectors.toList()); 139 assertEquals(area.minEast, entries.get(0).getKey().minEast, 1e-7); 140 assertEquals(5, entries.get(0).getKey().maxEast, 1e-7); 141 assertEquals(area.minNorth, entries.get(0).getKey().minNorth, 1e-7); 142 assertEquals(area.maxNorth, entries.get(0).getKey().maxNorth, 1e-7); 143 assertEquals(5, entries.get(1).getKey().minEast, 1e-7); 144 assertEquals(area.maxEast, entries.get(1).getKey().maxEast, 1e-7); 145 assertEquals(area.minNorth, entries.get(1).getKey().minNorth, 1e-7); 146 assertEquals(area.maxNorth, entries.get(1).getKey().maxNorth, 1e-7); 147 } 148 }