Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Displaying a huge map with good performance on Android?

I am a “beginner” who started Android development about 6 months ago. I am really impassioned on the topic, and I read several books:

  • Learn Java for Android Development;
  • Android Apps for Absolute Beginners;
  • Beginning Android 4 (Beginning Apress);
  • Pro Android 4 (Professional Apress);
  • Copies of Mark Murphy’s books (since no Kindle version is available on Amazon).

Those books enabled me to understand a lot the way Android works. But what lacks to me is now… experience. So I decided to contribute to a project, whose goal is to display a huge subway map—of course, the project’s goals are wider, but talking about it is not relevant to this question.

The map is stored offline, in my application’s package.

What to use?

  • Google Maps cannot be used, since my application’s map has little to nothing to do with Google Maps.
  • Using a simple ImageView to display the map is “not possible”, since zooming, panning, etc. are not supported, and coding this myself goes far beyond my capabilities.
  • The only nice workaround I have found out, is to use a WebView, since zooming and panning are supported.

Also, using only one big image cannot do the trick: it causes extreme image quality loss.

What the problems are

  • I cannot use a single image as Android automatically reduces the quality when a picture is above 1300px or so (see link above).
  • I want to display my map at a resolution of 2000px minimum (xhdpi screens display the whole image on their 1280px-wide screen, but I want them to be able to zoom as well).
    • Using multiple-density images (you know, different versions for ldpi, mdpi, hdpi and xhdpi devices) is not possible, insofar as they drastically increase the APK’s filesize…
  • By separating my map into 5 different tiles, and using only one image for all screen densities, everything seems not bad… except that WebView performance is awful.

That last solution’s code

I split my huge map into 5 different map tiles, which are basically rectangles in landscape format. I put them all (map[1-5].jpg), plus a HTML file (map.html), into the ./assets/ folder. The HTML file’s code is the following:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Huge map</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta name="viewport" content="user-scalable=yes, height=device-height, target-densityDpi=device-dpi">

    <style type="text/css">
    html, body, p, img { margin: 0; }
    img { border: 0; }
    p { height: 100%; min-height: 100%; }
    </style>
</head>
<body>
<p><!-- Displaying a 2000×2000px map -->
    <img src="file:///android_asset/map1.jpg" alt=""><br> <!-- 2000×408px -->
    <img src="file:///android_asset/map2.jpg" alt=""><br> <!-- 2000×408px -->
    <img src="file:///android_asset/map3.jpg" alt=""><br> <!-- 2000×408px -->
    <img src="file:///android_asset/map4.jpg" alt=""><br> <!-- 2000×408px -->
    <img src="file:///android_asset/map5.jpg" alt=""> <!-- 2000×368px -->
</p>
</body>
</html>

Which I then load in my Java code and apply it to my layout (which is but a WebView):

import me.diti.test.R;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.webkit.WebSettings;
import android.webkit.WebView;

public class MapActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.map); // ./res/layout/map.xml with a <WebView>

        WebView mWebView = (WebView) findViewById(R.id.mapWebView); // The WebView’s id is mapWebView

        WebSettings webSettings = mWebView.getSettings();
            webSettings.setRenderPriority(WebSettings.RenderPriority.HIGH); // Attempt at getting better performance…
            webSettings.setAppCacheEnabled(false); // Offline map: no need for cache
            webSettings.setLoadWithOverviewMode(true);
            webSettings.setUseWideViewPort(true);
            webSettings.setSupportZoom(true);
            webSettings.setBuiltInZoomControls(true);

        mWebView.setInitialScale(100);
        mWebView.setScrollBarStyle(WebView.SCROLLBARS_INSIDE_OVERLAY);

        mWebView.loadUrl("file:///android_asset/map.html"); // We load the map (includes 5 big images)

    }

/* This is a minimal “working” example, no need for menu inflation and activity handling */

App execution

  • At startup, the image(s) displays correctly;
  • Unzooming works well (any possibility to not see this white background? The CSS property min-height seems not to work);
  • Maximum zooming is ugly;
  • Performance while panning is horrible!

My question

What is the best way to display a huge, custom map, with good performance and relative easiness to use? Is my solution acceptable? How to fix the flaws? I read a lot of StackOverflow questions concerning all that (custom maps, WebView performance), but none of them helped me.

Looking forward to your help, thank you in advance.

like image 971
Diti Avatar asked May 06 '12 14:05

Diti


2 Answers

I think splitting the large image into tiles is a good approach, but I think you need to take it further than just 5 images. Try splitting it into 100 200x200 images in a 10x10 grid (or 64 250x250 images in an 8x8 etc). Then use a <table> to display them in your existing WebView.

<table cellpadding="0" cellspacing="0" border="0">
    <tr>
        <td><img src="file:///android_asset/map00.00.jpg" border="0"></td>
        <td><img src="file:///android_asset/map00.01.jpg" border="0"></td>
        ...
        <td><img src="file:///android_asset/map00.09.jpg" border="0"></td>
    </tr>
    <tr>
        <td><img src="file:///android_asset/map01.00.jpg" border="0"></td>
        <td><img src="file:///android_asset/map01.01.jpg" border="0"></td>
        ...
        <td><img src="file:///android_asset/map01.09.jpg" border="0"></td>
    </tr>
    ...
</table>
like image 64
dave.c Avatar answered Nov 08 '22 14:11

dave.c


Using a simple ImageView to display the map is “not possible”, since zooming, panning, etc. are not supported, and coding this myself goes far beyond my capabilities.

You can find third-party code for panning and zooming an ImageView, such as this one, though that probably will not help you.

What is the best way to display a huge, custom map, with good performance and relative easiness to use?

Use a tile-based map viewer. OSMDroid has some support for this.

like image 40
CommonsWare Avatar answered Nov 08 '22 13:11

CommonsWare