Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TilesOverlay showing tiles at wrong positions when scrolling the map #1509

Closed
mikebravoyes opened this issue Mar 3, 2020 · 36 comments
Closed
Assignees
Milestone

Comments

@mikebravoyes
Copy link

Issue Type

[x] Bug

Description and/or steps/code to reproduce the problem

I have written an app with a map component, based on osmdroid 6.1.2. As long as I use only one “basic map” (e.g. MAPNIK, HIKEBIKEMAP, OpenTopo, etc) everything is OK. But as soon as an additional TilesOverlay comes into play (e.g. PUBLIC_TRANSPORT) there is a very strange effect: When the map is zoomed or scrolled, suddenly appears a wild mixture of tiles from MAPNIK and PUBLIC_TRANSPORT at wrong positions, mainly on more or less empty regions, like the sea area. With every movement of the map, the picture changes unpredictable.

Here is a screenshot (North Sea area close to the coasts of the Netherlands and Germany), which demonstrates the effect. The dynamic behaviour during the scroll process can’t be visualized here, but the static conditions are clearly visible in the upper left region of the screen.

Screenshot_North_Sea

This effect remains, even after all tiles are downloaded completely - checked with TileStates.getUpToDate();
mapView.invalidate(); doesn't help either.

Code snippets:

// create background map
mapView = (MapView) findViewById(R.id.mapview);
mapView.setTileSource(TileSourceFactory.MAPNIK);

// create overlay map
MapTileProviderBasic tileProvider = new MapTileProviderBasic(getApplicationContext());
tileProvider.setTileSource(TileSourceFactory.PUBLIC_TRANSPORT);
TilesOverlay tilesOverlay = new TilesOverlay(tileProvider, this.getBaseContext());
tilesOverlay.setLoadingBackgroundColor(Color.TRANSPARENT);
mapView.getOverlays().add(tilesOverlay);

// enable zoom
mapView.setMultiTouchControls(true);
mapView.setBuiltInZoomControls(false);

// initialize position + zoom level
IMapController mapController = mapView.getController();
GeoPoint geoPoint = new GeoPoint(53.6, 5.3);  // north sea
mapController.setCenter(geoPoint);
mapController.setZoom(8.0);

and in the related layout.xml:

<org.osmdroid.views.MapView
	android:id="@+id/mapview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

Btw: Other overlay types, such as Copyright, ScaleBar, Grid, Compass, MiniMap, etc. don't cause these problems.

Any ideas, where this effect comes from and how to repair it?

Environment

Tested on different devices with android versions from 4.0.3, 4.1.2, 4.4.2, 8.0 up to 9.0, no difference concerning the effect

Version of osmdroid the issue relates to:

6.1.2

@matthiasdittmer
Copy link
Contributor

@mikebravoyes I noticed this effect too sometimes.

What happens if you zoom out or move the map just a little bit (programmatically or by hand)?

@mikebravoyes
Copy link
Author

@mat8854
Every little movement causes a replacement of the "crazy tiles" with other ones - not necessarily at the same locations.

If the map is invalidated periodically (e.g. by a CompassOverlay, which "fires" twice per second), we have some flickering tiles, coming and going irregularly all the time. It's a pity that I'm not able to catch this behaviour in a screenshot.

@monsieurtanuki
Copy link
Collaborator

I could not reproduce the bug with the demo app ("Sample withTiles Overlay").
I suggest you upgrade to the latest version of osmdroid.

@mikebravoyes mikebravoyes changed the title TilesOverlay showing tiles at wrong positions when scolling the map TilesOverlay showing tiles at wrong positions when scrolling the map Mar 3, 2020
@mikebravoyes
Copy link
Author

@monsieurtanuki
According to https://github.com/osmdroid/osmdroid/releases, the latest version is 6.1.5 from 14 Dec 2019, correct? I just updated to this version, but the effect is unchanged.

And I couldn't identify any remark in the respective release notes, that would refer to this behaviour.

@mikebravoyes
Copy link
Author

@monsieurtanuki
I have installed a screen recorder in order to demonstrate the effect. If the recorder is configured in that way, that it allows to overlay its controls over the app the be recorded, then the TilesOverlay is disappeared (!).

Anyway, here is the short film sequence in mp4 format and zipped:

20200303_164803.zip

In the first few seconds, I have scrolled the map manually, in the 2nd half it is updated by the CompassOverlay.

@monsieurtanuki
Copy link
Collaborator

Nice video ;)
My remarks:

  • as I said, I could not reproduce the bug, which means I cannot be 100% helpful
  • obviously, the "wrong" tiles are on top of correct tiles, which means the problem lies on the second tile overlay
  • for the sake of it, you could try to remove the second tile overlay and use only the map overlay BUT setting its source to your second tile source (PUBLIC_TRANSPORT). I guess it should work OK, but if we see random tiles then that's another story! You could also try to invert tile sources: my guess is that you'll get correct public transport tiles with garbage on top
  • it looks like a side-effect, but not a totally random one
    • we see strange things, but not irrelevant ones - always tiles that could potentially be correctly displayed on the map (e.g. in the tile cache), and always in the same "tile zones" on the screen
    • like if there are 2 TileOverlays, they kind of share a common bitmap pool
    • and they do: it's called BitmapPool! I'll have a look at it.

@monsieurtanuki
Copy link
Collaborator

Additional thoughts:

  • if I remember well, we only display what is in memory cache (each MapTileProviderBase having its own MapTileCache)
  • the memory cache is populated asynchronously by the providers (http, assets, ...)
  • if we have a small memory cache and we need space, we remove tiles from the cache
  • when we remove a tile from the memory cache, we recycle the bitmap from the shared bitmap pool, and asynchronously

@mikebravoyes My suggestion: for test purposes, could you please increase the size of the cache of both providers. Something like

provider.getTileCache().setAutoEnsureCapacity(false);
provider.getTileCache().ensureCapacity(100);

@matthiasdittmer
Copy link
Contributor

@monsieurtanuki
I have installed a screen recorder in order to demonstrate the effect. If the recorder is configured in that way, that it allows to overlay its controls over the app the be recorded, then the TilesOverlay is disappeared (!).

Anyway, here is the short film sequence in mp4 format and zipped:

20200303_164803.zip

In the first few seconds, I have scrolled the map manually, in the 2nd half it is updated by the CompassOverlay.

Okay, I watched the video. Your problem is even worse than the effect I had in mind.

@mikebravoyes
Copy link
Author

@monsieurtanuki
Thank you for your comments. I have a lot of remarks and new questions:

1.) Just a step aside: Talking about osmdroid versions, did you see my issue #1500 ?

2.) But back to our topic: You said you could not reproduce the problem with the demo app. Where can I find the source code of that demo? I have “stolen” my code from github here, but something essential must be different.

3.) You are talking about 2 TilesOverlay s. Maybe it’s just a matter of terminology, but we have just 1 “background map” (MapView with MAPNIK) plus 1 TilesOverlay (PUBLIC_TRANSPORT).

In the video we see some more overlays (Copyright, ScaleBar, Grid, Compass, MiniMap and NorthArrow), but they don’t affect the behaviour in question – except the fact, that the compass is triggering the periodic refresh of the whole map.

In the 1st screenshot above, there are only 2 layers: The MapView plus 1 TileOverlay. (The 2 icons in the upper right corner are defined in the layout.xml, not part of the map.)

4.) Two more observations:
a) The effect has to be started manually by scrolling the map. In the initial phase of the activity, it’s not there (even with CompassOverlay).
b) The “crazy tiles” have a clear preference for the open sea, but sometimes I could see some on the land area as well.

5.) Test with MapView = PUBLIC_TRANSPORT only
Surprise!! Once I have started the effect by manual scrolling of the map, and then leaving it still, I see a wild flickering of “crazy tiles” with an update rate of estimated 10 Hz. Note: There is no CompassOverlay at work. I reckon, the hecticness is triggered by the download of tiles, but that’s just a guess. The hourglass icon on the right hand indicates that the download is still in progress (17 of 35 tiles already loaded).

See screenshot
Screenshot_20200304-113340

and video
20200304_114405.zip

6.) Test with MapView (PUBLIC_TRANSPORT) plus TilesOverlay (MAPNIK) on top: As expected, the MAPNIK fills the whole area completely, without any gap, covering anything below. No “crazy tiles” visible. I don’t take a screenshot of that.

7.) Test with provider.getTileCache() as proposed by you: No difference in the effect.

8.) I made also tests with the entry in AndroidManifest.xml “android:hardwareAccelerated”. There is no difference, if it’s true or false.

@monsieurtanuki
Copy link
Collaborator

I saw #1500, but I have no clue about it.

About the demo app, it's SampleWithTilesOverlay

I've just managed to reproduce the bug with SampleWithTilesOverlay and PUBLIC_TRANSPORT as second tile overlay, on zoom level 8, centered on Zeeland.

Btw there's a mandatory (and kind of hidden) TileOverlay for each MapView. Then, if needed, you can TileOverlays on top.

@monsieurtanuki monsieurtanuki self-assigned this Mar 4, 2020
@monsieurtanuki
Copy link
Collaborator

@mikebravoyes Correct me if I'm wrong:

  • the crazy tiles are only visible where there shouldn't be anything at all regarding public transport, e.g. on the sea
  • this happened only when the public transport tile source was involved

@mikebravoyes
Copy link
Author

@monsieurtanuki
to 1.) No! They are concentrated on sea, but appear also on land areas. See video below:
20200304_153604.zip

to 2.) Yes! I have tested also OPEN_SEAMAP, FIETS_OVERLAY_NL and ROADS_OVERLAY_NL (the latter 2 with MapView = BASE_OVERLAY_NL). I could not see the effect here. Obviously, it only happens with PUBLIC_TRANSPORT.

@monsieurtanuki
Copy link
Collaborator

Why should it happen only with PUBLIC_TRANSPORT?

  • because not too long ago they had some problems and sent irrelevant tiles (e.g. wrong zoom levels), that are now cached on your system and not expired yet (mere hypothesis) => clearing the cache might be an option
  • because their tiles are special (e.g. very small), which brings a specific side effect

For the moment, I believe that there's an issue with the MapTileApproximater (more specifically its static method approximateTileFromLowerZoom(BitmapDrawable, long, int)), around its use of the BitmapPool, that is triggered by specific PUBLIC_TRANSPORT data for specific zoom levels (for instance, I see no bug on zoom level 7 and below).

Well, that's enough for today.

@mikebravoyes
Copy link
Author

@monsieurtanuki
I found the cache in the local storage on "osmdroid/tiles". There were two files, "cache.db" and "cache.db-journal". I deleted them both and tested again. No difference, sorry!

Concerning the tile size: As far as I can see during the population of the map with content, they have a similar size (if not identical) to the base map (MAPNIK).

OK, thanks for today!

@monsieurtanuki
Copy link
Collaborator

Well, I think I've got it. Partially.
When we use the MapTileApproximater, we get a bitmap from the pool (or we create a new one if the pool is empty). The idea is then to draw approximations on it, and then say "this is my approximation of the tile".
From the pool, we can get whatever tile bitmap we've already used before, which explains why we see familiar tiles (familiar, but not at the right place).
The problem is that we don't clear the tile from the pool before using it. That means that if we have no approximation to put on it, we display the bitmap we got from the pool as it was.
The fix is just to clear the bitmap when we get it from the pool: in the worst case we have a transparent bitmap.
Why does this happen only for public transport ? Perhaps the other tile sources have data even on the sea, which help drawing something (even transparent) on the bitmap.
Why does it blink? That's another issue: when using the approximater, we need computed bitmaps - they are in the cache, and if there are too many the garbage collector is called - bitmaps are cleared, we need to compute approximations, and so on.
Why is it a problem now, though the approximater was coded 2-3 years ago? I don't know...

The first step is to clear the bitmap when it comes from the pool: I can do it. Tomorrow ;)

@monsieurtanuki
Copy link
Collaborator

I think the problem is around providing, displaying or copying transparent bitmaps.
Like if Android had a trigger that said: "it doesn't make sense to write something totally transparent: I won't overwrite, I'll just dismiss the whole thing".

@mikebravoyes
Copy link
Author

To be honest, I understood not half of what you were saying yesterday. Anyway, I hope you are on the right track.

@monsieurtanuki
Copy link
Collaborator

Interesting. I did not expect to be crystal clear either, it's a work in progress.
By curiosity, could you please name 3 concepts I used that you puzzled you. I'd use that in order to write a short doc.

@mikebravoyes
Copy link
Author

@monsieurtanuki

Hmm, 3 puzzling concepts, let’s see:

1.) What is the bitmap pool – in contrast to the cache and its garbage collector? Is there a difference between tile and bitmap?

2.) What is the “approximation” of a tile? (If I had to guess, I would say it has to do with the zoom level.)

3.) Why has a tile from the pool to be cleared before using it and what has this to do with transparency?

I think, something like a flow diagram would be very helpful for the doc.

@mikebravoyes
Copy link
Author

@monsieurtanuki

Let me tell you about my professional background:

Recently, I was retired, but before that I was working for companies which produce ECDIS and VTS systems. Both of these have as major component a sea chart, defined by specifications from two international organisations: IMO and IALA.

Like OSM, such a sea chart is composed of different layers:
• one ground layer with filled, coloured areas (land, sea)
• coast lines and depth contours
• spot soundings (text indicating the water depth)
• navigation marks like buoys, wrecks
• prominent features on land (lighthouse, tower, church)
• dynamic layer (e.g. radar video)
• artefacts like scale, lat/lon grid, compass rose
• ship symbols, tracks, heading and course/speed vectors
All these layers except the first one are transparent; otherwise they would cover everything below.

For the composition of such a chart, you can buy an ECDIS kernel from companies like 7Cs (spoken “seven seas”) or you can do it yourself, like SAM Electronics. The chart material is generated by official authorities (in Germany: BSH) and distributed in encrypted format, because you have to pay for its usage.

From the technical point of view, the major difference is, that the map elements are given in object oriented format. For example a buoy is defined by its geographical position and some attributes, which specify its type. It says nothing about the graphical representation of such an object. This has been defined by a special “colours and symbols working group” and is affected by different parameters:
• Environmental conditions (bright day, dark day, bright night, dark night). When you have ever been on a ship’s bridge at night, you know that simply dimming the monitor brightness is not sufficient. The colour schemes are changed completely. His is well-known from car navigation systems or Google Maps, but more sophisticated.
• The map range (scale, zoom level). For example spot soundings have to be removed from a certain range; otherwise they would clutter the whole picture. Buoys have different shapes, depending on scale.
• User settings (on/off)

Every layer is composed of cells (similar to your tiles), which have attributes like the cell corners and the level of visibility. When a map is constructed, each cell is either disregarded (due to zoom level or user setting) or it is taken, transformed by the projection (mostly Mercator, but others are also possible), and saved in its respective layer, where it is finally displayed on the screen. That’s it – straightforward.

What I want to say: I’m rather familiar with the composition of a chart display, but I never heard of such thing as an “approximation of tiles”.

@monsieurtanuki
Copy link
Collaborator

What is the bitmap pool?

osmdroid spends its time displaying tiles.
Basically, those tiles were previously downloaded and stored into a local SQLite database, as a map tile index (zoom, x, y), a tile source tag, an expiration date, and binary data (the image). The primary key being the map tile index and the tile source tag.
osmdroid only displays tiles that are in the memory cache: "We need to display tile XYZ from tile source T, it's not in the memory cache, let's try to retrieve it from the database (or from the internet) and load it into memory".
The memory cache contains (XYZ,Bitmap) associations. And discards thoses associations during the garbage collecting, e.g. when the user panned the map to the east and some tiles of the west are not used anymore.
We don't need a bitmap pool. We could just create new Bitmaps then "destroy" them. But as we're dealing with tiles that all have the same size (typically, 256x256), we can reuse the 256x256 bitmap object. That's why we use a bitmap pool: in order to avoid destroying and recreating the same bitmap objects in order to have a smaller impact on memory. Especially on smartphones.
In short, that's about performance in the context of smartphones.

What is the “approximation” of a tile?

Typically, tiles are downloaded from the internet then stored in the local SQLite database.
Users start from scratch: they don't store all the tiles of all the zoom levels of all the tile sources: tiles are being downloaded when needed.
What if suddenly there's no network? Should we display a blank tile because we don't actually have the tile? Or maybe we could approximate the tile from a lower zoom: OK it will look a bit gross, with fat pixels, but that's better than looking at blank tiles.
There's also an "offline first" mode: if the network is slow, display the approximation while the actual "perfect" tile is being slowly downloaded (it will be displayed ASAP).
In short, that's about UX in the context of smartphones.

Why has a tile from the pool to be cleared before using it?

Work in progress (to be confirmed):

  • from the bitmap pool we get previously discarded tiles bitmap
  • when approximating, we copy (and stretch) the tile of a lower zoom into the bitmap from the bitmap pool
  • if there's an Android bug with this, for instance, if when you copy a transparent bitmap into a bitmap from the bitmap pool, maybe sometimes Android says "what's the point of copying a transparent image: I don't do it", and let the initial bitmap untouched

That would explain why you see unrelated bitmaps (from the pool) in places where you're supposed to see an approximation of a transparent tile.

@mikebravoyes
Copy link
Author

OK, understood - every single point!

Of course, there is a big difference between an ECDIS system and OSM on a smartphone. Not only the hardware platform, but also the strategy how the data is made available.

So please excuse my ignorance. At least, my assumption that "approximation" has to do with the zoom level, was correct.

monsieurtanuki added a commit that referenced this issue Mar 6, 2020
… images

Impacted classes:
* `DefaultOverlayManager`: unrelated minor fix
* `IMapTileProviderCallback`: unrelated minor refactoring
* `MapTileApproximater`: Android bug fixes for method `getTileBitmap` - when we get a bitmap from the pool, now we explicitly make it totally transparent
* `SampleWithTilesOverlay`: minor changes - `TileSourceFactory.PUBLIC_TRANSPORT`, `setLoadingBackgroundColor(Color.TRANSPARENT)` - in order to be able to reproduce the bug more easily if needed
@monsieurtanuki
Copy link
Collaborator

I'm glad it helped!
I wouldn't swear that all the other projects share the same terminology, though. Like "MapTileApproximater" :)
Well, as far as I'm concerned the bug is fixed by #1513. As I suspected, it's a fix for Android bugs regarding transparency.

monsieurtanuki added a commit that referenced this issue Mar 6, 2020
bug/#1509 - Android bug fixes for MapTileApproximater and transparent images
@mikebravoyes
Copy link
Author

Great! Natural laws are still in place ;-)

The wording "approximation" is in fact a little bit misleading. I would have called it substitute, surrogate, replacement or something like that. Anyway, if it's working, that's what counts.

Will there be a new release 6.1.6 soon, or how can I access the modified version?

@monsieurtanuki
Copy link
Collaborator

Personally I would do it like a pig: I would download the whole latest source code and copy it in my app.

@mikebravoyes
Copy link
Author

@monsieurtanuki
Following these instructions, after several hours I was able to succeed with the section "Building with Gradle". But the next step "Building osmdroid with Android Studio" failed with the following error message:

Unsupported Modules Detected
Compilation is not supported for following modules: osmdroid. Unfortunately you can't have non-Gradle Java modules and Android-Gradle modules in one project.

Rather frustrating experience - like poking around in the dark. I give up and wait for the next release. Sooner or later it will appear.

Thank you for your patient support. The communication with you was a real pleasure :-)

@spyhunter99
Copy link
Collaborator

spyhunter99 commented Mar 7, 2020 via email

@monsieurtanuki
Copy link
Collaborator

@spyhunter99 That would be great!

@mikebravoyes "Rather frustrating experience": I know exactly how you feel ;)

@monsieurtanuki
Copy link
Collaborator

@spyhunter99 Thank you for release 6.1.6!
@mikebravoyes Please test and close the issue if relevant.

@monsieurtanuki monsieurtanuki added this to the v6.1.6 milestone Mar 8, 2020
@mikebravoyes
Copy link
Author

@monsieurtanuki
@spyhunter99
I just have re-built my app with osmdroid 6.1.6. The bug is gone, everything OK.
Thank you very much to both of you for your help !!

@mikebravoyes
Copy link
Author

mikebravoyes commented Mar 10, 2020

@monsieurtanuki
I hate to say it, but now we have a problem with TileSourceFactory.HIKEBIKEMAP - not a TilesOverlay, just a "normal" base map. It doesn't load any tiles at all.

All other tile sources that I have tested, are OK. No idea, what is so special about this map type.

I checked with all osmdroid releases between 6.1.2 (still OK) and 6.1.6. The failure was obviously introduced earlier with 6.1.3. Its releases notes say:

minor bug fixes
esri shape viewer
simplified "speech balloon" demo

@monsieurtanuki
Copy link
Collaborator

Looks like an unrelated issue.
Please close this one and create a distinct one.

@mikebravoyes
Copy link
Author

@monsieurtanuki
Recently, I looked again at the map with MAPNIK plus overlay PUBLIC_TRANSPORT and - shock!! - now I see gaps in the overlay map. They are altering with the zoom level. See 2 screenshots below:

20200327-113536_Sensor Recording
20200327-113547_Sensor Recording

I'm still using osmdroid 6.1.6. Deleting the cache files did not make a difference.

Any ideas?

@monsieurtanuki
Copy link
Collaborator

What you say is that now, some tiles of PUBLIC_TRANSPORT never appear, am I correct? They don't disappear suddenly.
Couldn't it be a problem with the tile source itself?
I don't know what PUBLIC_TRANSPORT is all about: if it's about the current traffic, I guess they don't display that much traffic jams those days with everybody confined...

@mikebravoyes
Copy link
Author

mikebravoyes commented Mar 27, 2020

@monsieurtanuki
Concerning the tile source itself: I understood that PUBLIC_TRANSPORT does not show the current traffic situation, but just the routes of trains and buses.

Some tiles appear, and some don't. But the gaps are not constant. When I zoom in and out a little bit, the appearance toggles between the 2 screenshots above. That is probably not caused by the tile source, except there was something wrong with the tiles' attributes (e.g. visibility as a function of the zoom level).

At least the existing tiles are all in the right place.

@mikebravoyes
Copy link
Author

@monsieurtanuki
I see the same gaps in my app and on the website openptmap.org. Therefore, it's certainly not a problem of the osmdroid library, but of the tile provider itself. No Idea, what they are doing. I will close the issue again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants