Skip to content

Commit c8c2f90

Browse files
authored
Merge pull request #126 from mahomaps/tiles-fallback
Fallback missing tiles to lower zooms
2 parents 8592e93 + 48aeaa6 commit c8c2f90

File tree

5 files changed

+128
-16
lines changed

5 files changed

+128
-16
lines changed

src/mahomaps/Settings.java

+6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ private Settings() {
2828
public static boolean bbWifi;
2929
public static boolean firstLaunch = true;
3030
public static int map;
31+
/**
32+
* Reads "fallback" tile (scaled up) before downloading it.
33+
*/
34+
public static boolean readCachedBeforeDownloading = false;
3135

3236
public static String proxyServer = "http://nnp.nnchan.ru:80/mahoproxy.php?u=";
3337

@@ -94,6 +98,7 @@ public static synchronized boolean Read() {
9498
firstLaunch = j.getBoolean("1", true);
9599
usageFlags = j.getInt("tm", 0);
96100
map = j.getInt("map", 0);
101+
readCachedBeforeDownloading = j.getBoolean("upscale", false);
97102
return true;
98103
} catch (Throwable e) {
99104
e.printStackTrace();
@@ -137,6 +142,7 @@ public static String Serialize() {
137142
j.put("1", firstLaunch);
138143
j.put("tm", usageFlags);
139144
j.put("map", map);
145+
j.put("upscale", readCachedBeforeDownloading);
140146
return j.toString();
141147
}
142148

src/mahomaps/map/TileCache.java

+14-3
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,26 @@ public void paint(Graphics g, int tx, int ty) {
2929
Font f = Font.getFont(0, 0, 8);
3030
int vo = f.getHeight();
3131
g.setFont(f);
32+
Image i = img;
33+
if (i != null) {
34+
g.drawImage(i, tx, ty, 0);
35+
}
3236
if (state == STATE_READY) {
33-
Image i = img;
3437
if (i == null) // wtf!?
3538
throw new NullPointerException("Corrupted tile state!");
36-
g.drawImage(i, tx, ty, 0);
3739
g.setColor(0, 0, 255);
3840
} else {
41+
int ax = tx + 128;
42+
int ay = ty + 128 - (vo / 2);
43+
44+
g.setGrayScale(255);
45+
g.drawString(GetState(), ax + 1, ay + 1, Graphics.TOP | Graphics.HCENTER);
46+
g.drawString(GetState(), ax - 1, ay + 1, Graphics.TOP | Graphics.HCENTER);
47+
g.drawString(GetState(), ax + 1, ay - 1, Graphics.TOP | Graphics.HCENTER);
48+
g.drawString(GetState(), ax - 1, ay - 1, Graphics.TOP | Graphics.HCENTER);
49+
3950
g.setGrayScale(0);
40-
g.drawString(GetState(), tx + 128, ty + 128 - vo, Graphics.TOP | Graphics.HCENTER);
51+
g.drawString(GetState(), ax, ay, Graphics.TOP | Graphics.HCENTER);
4152
}
4253
if (Settings.drawDebugInfo) {
4354
g.drawRect(tx, ty, 255, 255);

src/mahomaps/map/TilesProvider.java

+103-12
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import mahomaps.api.YmapsApiBase;
2424
import mahomaps.overlays.TileCacheForbiddenOverlay;
2525
import mahomaps.overlays.TileDownloadForbiddenOverlay;
26+
import tube42.lib.imagelib.ImageUtils;
2627
import mahomaps.overlays.CacheFailedOverlay;
2728

2829
public class TilesProvider implements Runnable {
@@ -77,6 +78,10 @@ public static final String[] GetLayerNames() {
7778
return s;
7879
}
7980

81+
public String GetLocalPath() {
82+
return localPath;
83+
}
84+
8085
public TilesProvider(String lang) {
8186
if (lang == null)
8287
throw new NullPointerException("Language must be non-null!");
@@ -184,18 +189,40 @@ public void RunCache() {
184189
}
185190
}
186191

187-
Image img = null;
188-
if (Settings.cacheMode == Settings.CACHE_FS && localPath != null) {
189-
img = tryLoadFromFS(tc);
190-
} else if (Settings.cacheMode == Settings.CACHE_RMS) {
191-
img = tryLoadFromRMS(tc);
192+
Image img = tryLoad(tc);
193+
boolean tryDownloadAnyway = false;
194+
195+
// if tile is not cached...
196+
if (img == null) {
197+
// and cache lookup actually was attempted...
198+
if (Settings.cacheMode != Settings.CACHE_DISABLED) {
199+
// but we can't download it
200+
if (!Settings.allowDownload) {
201+
// let's try lower zooms and upscale them.
202+
img = tryLoadFallback(tc);
203+
}
204+
// or scaling explicitly allowed before downloading
205+
else if (Settings.readCachedBeforeDownloading) {
206+
// let's try lower zooms and upscale them.
207+
img = tryLoadFallback(tc);
208+
// but still attempt to download
209+
tryDownloadAnyway = true;
210+
}
211+
}
192212
}
193213

194214
synchronized (tc) {
195215
if (img != null) {
196216
tc.img = img;
197-
tc.state = TileCache.STATE_READY;
198-
MahoMapsApp.GetCanvas().requestRepaint();
217+
218+
// readCachedBeforeDownloading is still require server lookup
219+
if (tryDownloadAnyway) {
220+
tc.state = TileCache.STATE_SERVER_PENDING;
221+
downloadGate.Reset();
222+
} else {
223+
tc.state = TileCache.STATE_READY;
224+
MahoMapsApp.GetCanvas().requestRepaint();
225+
}
199226
} else if (Settings.allowDownload) {
200227
tc.state = TileCache.STATE_SERVER_PENDING;
201228
downloadGate.Reset();
@@ -211,6 +238,50 @@ public void RunCache() {
211238
}
212239
}
213240

241+
private final Image tryLoad(TileId id) {
242+
if (Settings.cacheMode == Settings.CACHE_FS && localPath != null) {
243+
return tryLoadFromFS(id);
244+
} else if (Settings.cacheMode == Settings.CACHE_RMS) {
245+
return tryLoadFromRMS(id);
246+
}
247+
return null;
248+
}
249+
250+
private final Image tryLoadFallback(TileId id) {
251+
final int map = id.map;
252+
int zoom = id.zoom;
253+
int downscale = 1; // how small is required tile comparing to loaded one
254+
int x = id.x;
255+
int y = id.y;
256+
int xoff = 0; // in tile sizes
257+
int yoff = 0;
258+
while (true) {
259+
zoom -= 1;
260+
if (zoom < 0 || downscale >= 64)
261+
return null;
262+
if (x % 2 == 1)
263+
xoff += downscale;
264+
if (y % 2 == 1)
265+
yoff += downscale;
266+
// at downscale of 2 above ops require 1, at 4 - 2 and so on
267+
downscale *= 2;
268+
x /= 2;
269+
y /= 2;
270+
Image raw = tryLoad(new TileId(x, y, zoom, map));
271+
if (raw != null) {
272+
int size = (256 / downscale); // of tile
273+
// System.out.println("downscale: " + downscale + ", size: " + size);
274+
// System.out.println("x: " + xoff + ", y: " + yoff);
275+
int xoffp = xoff * size; // in pixels
276+
int yoffp = yoff * size;
277+
Image cropped = ImageUtils.crop(raw, xoffp, yoffp, xoffp + size, yoffp + size);
278+
raw = null; // to free heap
279+
return ImageUtils.resize(cropped, 256, 256, true, false);
280+
}
281+
}
282+
283+
}
284+
214285
public void RunNetwork() {
215286
try {
216287
// цикл обработки
@@ -284,6 +355,20 @@ else if (l != s)
284355
}
285356

286357
Image img = Settings.allowDownload ? download(tc) : null;
358+
boolean fallbackUsed = false;
359+
360+
// if nothing loaded
361+
if (img == null) {
362+
// if the cache available...
363+
if (Settings.cacheMode != Settings.CACHE_DISABLED) {
364+
// let's try lower zooms and upscale them.
365+
// If download is forbidden, this happened in cache thread and this path won't
366+
// run at all.
367+
img = tryLoadFallback(tc);
368+
if (img != null)
369+
fallbackUsed = true;
370+
}
371+
}
287372

288373
boolean waitAfterError = false;
289374

@@ -297,7 +382,13 @@ else if (l != s)
297382
}
298383
} else {
299384
tc.img = img;
300-
tc.state = TileCache.STATE_READY;
385+
if (fallbackUsed) {
386+
// image is present, but it's bad. We still want to download a proper one.
387+
tc.state = TileCache.STATE_ERROR;
388+
waitAfterError = true;
389+
} else {
390+
tc.state = TileCache.STATE_READY;
391+
}
301392
MahoMapsApp.GetCanvas().requestRepaint();
302393
}
303394
}
@@ -306,8 +397,10 @@ else if (l != s)
306397
Thread.sleep(4000);
307398
}
308399

309-
if (idleCount != cache.size() || queueChanged)
400+
if (idleCount != cache.size() || queueChanged) {
401+
Thread.yield();
310402
continue;
403+
}
311404
downloadGate.Pass();
312405
}
313406
} catch (InterruptedException e) {
@@ -389,7 +482,6 @@ private Image download(TileId id) throws InterruptedException {
389482
} catch (RuntimeException e1) {
390483
}
391484
} catch (Exception e) {
392-
e.printStackTrace();
393485
} catch (OutOfMemoryError e) {
394486
} finally {
395487
if (hc != null) {
@@ -586,8 +678,7 @@ public TileCache getTile(TileId tileId) {
586678
}
587679

588680
private String getUrl(TileId tileId) {
589-
String url = tilesUrls[tileId.map] + lang + "&x=" + tileId.x + "&y=" + tileId.y
590-
+ "&z=" + tileId.zoom;
681+
String url = tilesUrls[tileId.map] + lang + "&x=" + tileId.x + "&y=" + tileId.y + "&z=" + tileId.zoom;
591682
if (Settings.proxyTiles && url.startsWith("https")) {
592683
return Settings.proxyServer + YmapsApiBase.EncodeUrl(url);
593684
}

src/mahomaps/screens/CacheManager.java

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public CacheManager(TilesProvider tiles) {
2626
setCommandListener(this);
2727
append(new StringItem(MahoMapsApp.text[71], getCacheType()));
2828
append(new StringItem(MahoMapsApp.text[72], "" + tiles.GetCachedTilesCount()));
29+
if (Settings.cacheMode == Settings.CACHE_FS)
30+
append(new StringItem(MahoMapsApp.text[71], tiles.GetLocalPath()));
2931
delAll.setLayout(Item.LAYOUT_NEWLINE_AFTER | Item.LAYOUT_NEWLINE_BEFORE);
3032
delAll.setDefaultCommand(sel);
3133
delAll.setItemCommandListener(this);

src/mahomaps/screens/SettingsScreen.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public class SettingsScreen extends Form implements CommandListener {
2727
private ChoiceGroup cache = new ChoiceGroup(MahoMapsApp.text[52], Choice.POPUP,
2828
new String[] { MahoMapsApp.text[18], MahoMapsApp.text[53], "RMS" }, null);
2929
private ChoiceGroup download = new ChoiceGroup(MahoMapsApp.text[54], Choice.MULTIPLE,
30-
new String[] { MahoMapsApp.text[17] }, null);
30+
new String[] { MahoMapsApp.text[17], "Upscale existing cache before downloading" }, null);
3131
private ChoiceGroup proxyUsage = new ChoiceGroup(MahoMapsApp.text[19], Choice.MULTIPLE,
3232
new String[] { MahoMapsApp.text[156], MahoMapsApp.text[157], }, null);
3333
private TextField proxyServer = new TextField(MahoMapsApp.text[158], "", 256, TextField.URL);
@@ -72,6 +72,7 @@ public SettingsScreen() {
7272
tileInfo.setSelectedIndex(Settings.drawDebugInfo ? 1 : 0, true);
7373
cache.setSelectedIndex(Settings.cacheMode, true);
7474
download.setSelectedIndex(0, Settings.allowDownload);
75+
download.setSelectedIndex(1, Settings.readCachedBeforeDownloading);
7576
proxyUsage.setSelectedIndex(0, Settings.proxyTiles);
7677
proxyUsage.setSelectedIndex(1, Settings.proxyApi);
7778
uiSize.setSelectedIndex(Settings.uiSize, true);
@@ -103,6 +104,7 @@ private void Apply() {
103104
Settings.drawDebugInfo = tileInfo.getSelectedIndex() == 1;
104105
Settings.cacheMode = cache.getSelectedIndex();
105106
Settings.allowDownload = download.isSelected(0);
107+
Settings.readCachedBeforeDownloading = download.isSelected(1);
106108
Settings.proxyTiles = proxyUsage.isSelected(0);
107109
Settings.proxyApi = proxyUsage.isSelected(1);
108110
Settings.uiSize = uiSize.getSelectedIndex();

0 commit comments

Comments
 (0)