p-lr / MapView

A Fast, memory efficient Android library to display tiled maps, with support for markers, paths, and rotation.
Apache License 2.0
184 stars 38 forks source link

BitmapFactory: bitmap marked for reuse can't fit new bitmap #4

Closed danramteke closed 4 years ago

danramteke commented 4 years ago

Hello,

I keep getting these warnings, and nothing shows in the MapView on Emulator.

W/BitmapFactory: bitmap marked for reuse (32768 bytes) can't fit new bitmap (65536 bytes)

I reduced my tile size from 512x512 to 128x128 and I still get the same error.

Does anyone know a workaround?

EDIT: by the way I'm migrating from Moagrius TileView which I was able to use th 512x512 tiles with.

p-lr commented 4 years ago

Do you mean that you're not able to use 512x512 tiles? Also, could you show me how you configure the MapView (I'd like to check).

danramteke commented 4 years ago

Thanks for the quick reply! I am not able to use the 128x128 tiles, nor am I able to use the 512x512 tiles either. I thought maybe using smaller tiles would help fix the BitmapFactory warning.

Sure, here is the vips command I ran (mac)

vips dzsave /path/to/my/map.png path/to/android/assets/tiles --layout=google --tile-size=128 --suffix .png --vips-progress

here is the code from the onCreate method in my Activity.

        val mapView = MapView(this)
        mapView.setBackgroundColor(resources.getColor(R.color.colorMapBackground, theme))
        val config = MapViewConfiguration(levelCount = 7, fullWidth = 12876, fullHeight = 15624,
            tileSize = 128, tileStreamProvider = MyTileStreamProvider(WeakReference(this.assets))
        ).setStartScale(1f)
            .setMaxScale(2f)
            .setMagnifyingFactor(1)
        mapView.configure(config)

        mapView.scale = 1f

        findViewById<RelativeLayout>(R.id.map_container).addView(mapView)
        this.mapView = mapView

and here is the code for MyTileStreamProvider:

class MyTileStreamProvider(private val weakAssetManager: WeakReference<AssetManager>): TileStreamProvider {
    override fun getTileStream(row: Int, col: Int, zoomLvl: Int): InputStream? {
        return try {
            val filename = "tiles/${zoomLvl}/${row}/${col}.png"
            weakAssetManager.get()!!.open(filename)
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }
}

Finally, here is the xml for the layout:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="MyApp.MyMainActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:id="@+id/map_container"
        >

   <com.google.android.material.floatingactionbutton.FloatingActionButton .../>
   <com.google.android.material.floatingactionbutton.FloatingActionButton .../>

  </RelativeLayout>
</LinearLayout>
p-lr commented 4 years ago

Alright, I'm going to check on that. For my information, have you tried the (default) 256 size?

p-lr commented 4 years ago

I've found the issue. The tileSize property wasn't taken into account properly. It's fixed in the version 1.0.8 Can you try it out and tell me if it's working for you?

A new major version of MapView is coming out soon. I'm going to integrate this fix into the next version.

danramteke commented 4 years ago

awesome! I'll try it out tonight

danramteke commented 4 years ago

Hmm - I haven't been able to get it to work.

I created a small demo project, with just the map and this MapView component in it. I added you as a collaborator.

danramteke commented 4 years ago

Update - I switch from .png to using .jpg in vips, and now the MapView component works!

p-lr commented 4 years ago

Nice! When MapView 2.0.0 will be ready I will do a PR in your repo if you want. Thanks for submitting this issue - it looks like you're the first one to try a different time size than 256.

danramteke commented 4 years ago

is there any chance of supporting PNG?

And any plans for extensibility? Currently, MapView isn't subclassable; and although MarkerLayout is subclassable, its method processHit isn't overridable. I want something that sends "processHit" to a callback with the current x, y, and zoom.

p-lr commented 4 years ago

I've tested png tiles yesterday, it works. I can make MapView subclassable, no problem. Same for processHit, I can do that. All of this will be available in 2.0.0

danramteke commented 4 years ago

Thanks so much! Your library runs smoother than the library I'm migrating from. Looking forward to 2.0.0!

p-lr commented 4 years ago

I'm polishing it before the release. All's left is the readme update. Should be done by this weekend.

p-lr commented 4 years ago

I've published the version 2.0.0-beta03

The API around map rotation isn't stable yet (It's working, but I might make changes). 2.x.x is a major shift from 1.x.x as it enables map rotation. It's only an opt-in feature, while configuring the MapView, using enableRotation(). The RotatingMapFragment from the demo is an example. But I don't recommend that you use this for now.

Beware that there are some breaking changes. Most of the time, it's just a package name change. The ScaleChangeListener interface has been removed. If you relied on this, I can explain how to do the equivalent in 2.x.x

p-lr commented 4 years ago

BTW, I'm closing this. Any feedback about the 2.0.0-beta will be appreciated (you can open other issues).

danramteke commented 4 years ago

One bit of feedback: When the app loads, the map is blank until I scroll. This wasn't the case in 1.0.8. I can't quite figure out if there is some way to make the map display without the user needing to scroll? Here is an example: https://github.com/danramteke/PLMapViewStudy/tree/upgrade-2.0.0

Other than that, this is wonderful! I'm able to get the tap information I need now that MapView is subclassable

danramteke commented 4 years ago

hmm I seem to have somehow gotten around the map loading as blank. I run a timer to poll if the map view has finished rendering, by comparing its height and width to 0

        val config = MapViewConfiguration(...).setStartScale(0.5f)
        mapView.configure(config)
        Timer().schedule(object : TimerTask() {
            override fun run() {
                if (mapView.width != 0 && mapView.height != 0) {
                    mapView.scrollToDefaultLocation()
                    this.cancel()
                }
            }
        }, 1, 5)

Thanks so much for this update!

p-lr commented 4 years ago

Ok good to know. Thanks for the feedback.

p-lr commented 4 years ago

Yes I've thought of that. Although the problem you had, especially with setStartScale config, reveals that there's something to be fixed.

Le mar. 28 janv. 2020 à 23:29, Daniel Ramteke notifications@github.com a écrit :

Would it be possible to call a callback method when the MapView has finished rendering? This callback would replace polling with a Timer.

— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub https://github.com/peterLaurence/MapView/issues/4?email_source=notifications&email_token=ADXKCCQUURC5WAJBYEOVCHLRACWWDA5CNFSM4KJIS2KKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEKFF6JA#issuecomment-579493668, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADXKCCQA66LVRRYPGKMRC23RACWWDANCNFSM4KJIS2KA .

p-lr commented 4 years ago

@danramteke I've been able to reproduce your issue with blank MapView in the beta. It was caused by the config setStartScale(1f). Any other scale would have worked. It's fixed now, and I've published the version 2.0.0-beta04 Does this eliminates your need of callback?

BTW, I've created a "MapView 2.0.0 project" here: https://github.com/peterLaurence/MapView/projects/1 Do you have access to it?

Thanks again for your valuable feedback!

danramteke commented 4 years ago

Hello, I updated to beta05, but I still need the timer to wait for the MapView to not have 0 width/height so that I can scroll to the default location in the viewport.

No worries though, the users I've talked to like the new MapView.

Yes, I have access to that project link.

p-lr commented 4 years ago

Alright, I will have a look at your project, and see if there is an issue or if an API is missing.

Le sam. 1 févr. 2020 à 20:52, Daniel Ramteke notifications@github.com a écrit :

Hello, I updated to beta05, but I still need the timer to wait for the MapView to not have 0 width/height so that I can scroll to the default location in the viewport.

No worries though, the users I've talked to like the new MapView.

Yes, I have access to that project link.

— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub https://github.com/peterLaurence/MapView/issues/4?email_source=notifications&email_token=ADXKCCXWFNUQLT5KEGBHFULRAXHITA5CNFSM4KJIS2KKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEKRFHKY#issuecomment-581063595, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADXKCCVBPOKTXT2DWPKP57LRAXHITANCNFSM4KJIS2KA .

p-lr commented 4 years ago

It looks like your project has been removed. If I understood correctly, you need to scroll to a specific position as soon as the MapView is ready. If I had to do this, I would do it inside onStart() lifecycle of the fragment. But I can't be sure - ideally I should try your code.

p-lr commented 4 years ago

I cannot reproduce your issue. If you look at MapAloneFragment .. and if you do this:

...
return MapView(context).apply {
     configure(config)
     scale = 0.5f
     scrollTo(3000, 3000)
}

It works as expected. I would recommend to follow the demo snippet as much as possible (adding the MapView inside onCreateView, assign an id, etc.).

danramteke commented 4 years ago

No worries, the people using my app are happy with how it's working. Thanks so much for your help!

Centering on default location

Yes it scrolls to the coordinates. The difference is that it scrolls to the top left corner instead of the center. A timer fixes this.

val mapView = MapView(context).apply {
     configure(config)
     scale = 0.5f
    scrollTo(3000 - width/2.0, 3000 - height/2.0)
}

vs

val mapView = MapView(context).apply {
     configure(config)
     scale = 0.5f
}

// ...

Timer().schedule(object : TimerTask() {
  override fun run() {
    if (mapView.width != 0 && mapView.height != 0) {
      this.cancel()
      scrollTo(3000 - mapView.width/2.0, 3000 - mapView.height/2.0)
    }
  }
}, 1, 5)

Using onStart Callback

Also, I looked into onStart and that won't work for me since onStart is called every time the app comes back from the background.

Sample project

Hmm - not sure why it's missing. There's a lot of things I need to take care of today. If I have time, I'll look into why it's missing.

p-lr commented 4 years ago

Thank you for this detailed answer. Now I understand the confusion. I've pushed a new branch [bounds](https://github.com/danramteke/PLMapViewStudy/tree/bounds)

It shows you how to scroll to a percent of the map's width and height, not using a timer and right after the configuration.