source: josm/trunk/src/org/openstreetmap/josm/gui/layer/imagery/ReprojectionTile.java@ 11883

Last change on this file since 11883 was 11883, checked in by bastiK, 7 years ago

see #7427 - actually use disk cache on scale change and do not force redownload

  • Property svn:eol-style set to native
File size: 7.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.layer.imagery;
3
4import java.awt.Dimension;
5import java.awt.geom.Point2D;
6import java.awt.image.BufferedImage;
7
8import org.openstreetmap.gui.jmapviewer.Tile;
9import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
10import org.openstreetmap.josm.Main;
11import org.openstreetmap.josm.data.ProjectionBounds;
12import org.openstreetmap.josm.data.coor.EastNorth;
13import org.openstreetmap.josm.data.projection.Projection;
14import org.openstreetmap.josm.data.projection.Projections;
15import org.openstreetmap.josm.tools.ImageWarp;
16import org.openstreetmap.josm.tools.Utils;
17
18/**
19 * Tile class that stores a reprojected version of the original tile.
20 * @since 11858
21 */
22public class ReprojectionTile extends Tile {
23
24 protected TileAnchor anchor;
25 private double nativeScale;
26 protected boolean maxZoomReached;
27
28 /**
29 * Constructs a new {@code ReprojectionTile}.
30 * @param source sourec tile
31 * @param xtile X coordinate
32 * @param ytile Y coordinate
33 * @param zoom zoom level
34 */
35 public ReprojectionTile(TileSource source, int xtile, int ytile, int zoom) {
36 super(source, xtile, ytile, zoom);
37 }
38
39 /**
40 * Get the position of the tile inside the image.
41 * @return the position of the tile inside the image
42 * @see #getImage()
43 */
44 public TileAnchor getAnchor() {
45 return anchor;
46 }
47
48 public double getNativeScale() {
49 return nativeScale;
50 }
51
52 public boolean needsUpdate(double currentScale) {
53 if (Utils.equalsEpsilon(nativeScale, currentScale))
54 return false;
55 if (maxZoomReached && currentScale < nativeScale)
56 // zoomed in even more - max zoom already reached, so no update
57 return false;
58 return true;
59 }
60
61 @Override
62 public void setImage(BufferedImage image) {
63 if (image == null) {
64 reset();
65 } else {
66 transform(image);
67 }
68 }
69
70 /**
71 * Invalidate tile - mark it as not loaded.
72 */
73 public synchronized void invalidate() {
74 this.loaded = false;
75 this.loading = false;
76 this.error = false;
77 this.error_message = null;
78 }
79
80 private synchronized void reset() {
81 this.image = null;
82 this.anchor = null;
83 this.maxZoomReached = false;
84 }
85
86 public void transform(BufferedImage imageIn) {
87 if (!Main.isDisplayingMapView()) {
88 reset();
89 return;
90 }
91 double scaleMapView = Main.map.mapView.getScale();
92 ImageWarp.Interpolation interpolation;
93 switch (Main.pref.get("imagery.warp.interpolation", "bilinear")) {
94 case "nearest_neighbor":
95 interpolation = ImageWarp.Interpolation.NEAREST_NEIGHBOR;
96 break;
97 default:
98 interpolation = ImageWarp.Interpolation.BILINEAR;
99 }
100 double margin = interpolation.getMargin();
101
102 Projection projCurrent = Main.getProjection();
103 Projection projServer = Projections.getProjectionByCode(source.getServerCRS());
104 EastNorth en00Server = new EastNorth(source.tileXYtoProjected(xtile, ytile, zoom));
105 EastNorth en11Server = new EastNorth(source.tileXYtoProjected(xtile + 1, ytile + 1, zoom));
106 ProjectionBounds pbServer = new ProjectionBounds(en00Server);
107 pbServer.extend(en11Server);
108 // find east-north rectangle in current projection, that will fully contain the tile
109 ProjectionBounds pbTarget = projCurrent.getEastNorthBoundsBox(pbServer, projServer);
110
111 // add margin and align to pixel grid
112 double minEast = Math.floor(pbTarget.minEast / scaleMapView - margin) * scaleMapView;
113 double minNorth = -Math.floor(-(pbTarget.minNorth / scaleMapView - margin)) * scaleMapView;
114 double maxEast = Math.ceil(pbTarget.maxEast / scaleMapView + margin) * scaleMapView;
115 double maxNorth = -Math.ceil(-(pbTarget.maxNorth / scaleMapView + margin)) * scaleMapView;
116 ProjectionBounds pbTargetAligned = new ProjectionBounds(minEast, minNorth, maxEast, maxNorth);
117
118 Dimension dim = getDimension(pbTargetAligned, scaleMapView);
119 Integer scaleFix = limitScale(source.getTileSize(), Math.sqrt(dim.getWidth() * dim.getHeight()));
120 double scale = scaleFix == null ? scaleMapView : (scaleMapView * scaleFix);
121
122 ImageWarp.PointTransform pointTransform = pt -> {
123 EastNorth target = new EastNorth(pbTargetAligned.minEast + pt.getX() * scale,
124 pbTargetAligned.maxNorth - pt.getY() * scale);
125 EastNorth sourceEN = projServer.latlon2eastNorth(projCurrent.eastNorth2latlon(target));
126 double x = source.getTileSize() *
127 (sourceEN.east() - pbServer.minEast) / (pbServer.maxEast - pbServer.minEast);
128 double y = source.getTileSize() *
129 (pbServer.maxNorth - sourceEN.north()) / (pbServer.maxNorth - pbServer.minNorth);
130 return new Point2D.Double(x, y);
131 };
132
133 // pixel coordinates of tile origin and opposite tile corner inside the target image
134 // (tile may be deformed / rotated by reprojection)
135 EastNorth en00Current = projCurrent.latlon2eastNorth(projServer.eastNorth2latlon(en00Server));
136 EastNorth en11Current = projCurrent.latlon2eastNorth(projServer.eastNorth2latlon(en11Server));
137 Point2D p00Img = new Point2D.Double(
138 (en00Current.east() - pbTargetAligned.minEast) / scale,
139 (pbTargetAligned.maxNorth - en00Current.north()) / scale);
140 Point2D p11Img = new Point2D.Double(
141 (en11Current.east() - pbTargetAligned.minEast) / scale,
142 (pbTargetAligned.maxNorth - en11Current.north()) / scale);
143
144 BufferedImage imageOut = ImageWarp.warp(
145 imageIn, getDimension(pbTargetAligned, scale), pointTransform,
146 interpolation);
147 synchronized (this) {
148 this.image = imageOut;
149 this.anchor = new TileAnchor(p00Img, p11Img);
150 this.nativeScale = scale;
151 this.maxZoomReached = scaleFix != null;
152 }
153 }
154
155 private Dimension getDimension(ProjectionBounds bounds, double scale) {
156 return new Dimension(
157 (int) Math.round((bounds.maxEast - bounds.minEast) / scale),
158 (int) Math.round((bounds.maxNorth - bounds.minNorth) / scale));
159 }
160
161 /**
162 * Make sure, the image is not scaled up too much.
163 *
164 * This would not give any significant improvement in image quality and may
165 * exceed the user's memory. The correction factor is a power of 2.
166 * @param lenOrig tile size of original image
167 * @param lenNow (averaged) tile size of warped image
168 * @return factor to shrink if limit is exceeded; 1 if it is already at the
169 * limit, but no change needed; null if it is well below the limit and can
170 * still be scaled up by at least a factor of 2.
171 */
172 protected Integer limitScale(double lenOrig, double lenNow) {
173 final double limit = 3;
174 if (lenNow > limit * lenOrig) {
175 int n = (int) Math.ceil((Math.log(lenNow) - Math.log(limit * lenOrig)) / Math.log(2));
176 int f = 1 << n;
177 double lenNowFixed = lenNow / f;
178 if (lenNowFixed > limit * lenOrig) throw new AssertionError();
179 if (lenNowFixed <= limit * lenOrig / 2) throw new AssertionError();
180 return f;
181 }
182 if (lenNow > limit * lenOrig / 2)
183 return 1;
184 return null;
185 }
186}
Note: See TracBrowser for help on using the repository browser.