Map API

Nutiteq Maps Lib SDK enables developing advanced mobile mapping applications.
Platforms: Android, RIM BlackBerry, Java ME (J2ME)
Download maps API SDK »
Untitled Document

Products

» View demos
» Downloads

Latest news

Last at Tue, 6 Dec 2011 18:37:53
Topic: New BlackBerry sample app
See more news »

blackberry sample app stops downloading

8 replies [Last post]
Kallin
User offline. Last seen 2 years 20 weeks ago. Offline
Joined: 09/14/2009

Hi everyone,
I just downloaded the nutiteq libs to see how it works in my blackberry project and i'm having issues with the sample blackberry app. The tiles come in OK from openstreetmap for a bit, but then seem to just stop. I enabled the download monitor and I saw it get up to about 500kb and then the download monitor disappeared.

If I restart the app it seems to work just fine again. I wonder if maybe the http connection timeout flag is not being set. I've found before that I need to do a little trick when opening connections:


while (true) {
try {
streamConnection = (StreamConnection) Connector.open(url.toString() + ";deviceside=true", Connector.READ_WRITE, true);
break;
} catch (IOException e) {
Logger.log("connection timed out:" + url);
}
}

My code is pretty much identical to the sample app, but i'll post it anyways:

public MapDemoScreen() {
final DefaultDownloadStreamOpener opener = new DefaultDownloadStreamOpener(";deviceside=true");
mapComponent = new MapComponent("abcdtrial", "Nutiteq", "WrappedRIMUIDemo", SCREEN_WIDTH,
SCREEN_HEIGHT, new WgsPoint(-74.0, 40.717), 5);
final UserDefinedKeysMapping keysMapping = new UserDefinedKeysMapping();
keysMapping.defineKey(ControlKeys.MOVE_UP_KEY, 84);
keysMapping.defineKey(ControlKeys.MOVE_UP_KEY, 69);

keysMapping.defineKey(ControlKeys.MOVE_DOWN_KEY, 66);
keysMapping.defineKey(ControlKeys.MOVE_DOWN_KEY, 88);
keysMapping.defineKey(ControlKeys.MOVE_LEFT_KEY, 83);
keysMapping.defineKey(ControlKeys.MOVE_RIGHT_KEY, 70);
keysMapping.defineKey(ControlKeys.MOVE_RIGHT_KEY, 74);

keysMapping.defineKey(ControlKeys.ZOOM_OUT_KEY, 20);
keysMapping.defineKey(ControlKeys.ZOOM_OUT_KEY, 65);
keysMapping.defineKey(ControlKeys.ZOOM_OUT_KEY, 79);

keysMapping.defineKey(ControlKeys.ZOOM_IN_KEY, 261);
keysMapping.defineKey(ControlKeys.ZOOM_IN_KEY, 81);
keysMapping.defineKey(ControlKeys.ZOOM_IN_KEY, 73);

mapComponent.enableDownloadCounter();
mapComponent.enableDownloadDisplay();
mapComponent.showZoomLevelIndicator(true);
mapComponent.showDefaultOnScreenZoomControls();
mapComponent.setControlKeysHandler(keysMapping);
mapComponent.setDownloadStreamOpener(opener);
mapComponent.setMapListener(this);
mapComponent.showZoomLevelIndicator(true);
// mapComponent.addKmlService(new KmlUrlReader(
// "http://www.panoramio.com/panoramio.kml?LANG=en_US.utf8", true));
// mapComponent.addKmlService(new KmlUrlReader(
// "http://lbs.nutiteq.ee/mps/kmlpf.php?friend=555111,555222,555333,", false));

mapComponent.startMapping();

testPlace = new Place(0, "Test Place", new TestPlacemark(), new WgsPoint(-74.0, 40.717));
mapComponent.addPlace(testPlace);
}

public void paint(final Graphics g) {
//wrap the graphics object inside library Graphics
if (wrapped != g) {
wrapped = g;
graphicsWrapper = new com.nutiteq.wrappers.rimui.Graphics(g);
}

//paint on wrapper (in effect painting on native graphics)
mapComponent.paint(graphicsWrapper);
//remove pushContext() done inside library
graphicsWrapper.popAll();
}

protected boolean navigationMovement(final int dx, final int dy, final int status, final int time) {
mapComponent.panMap(dx * 10, dy * 10);
invalidate();
return true;
}

I'd appreciate any suggestions. I am really interesting in moving our app over to this library as it should make it easier to port in the future.

Kallin
User offline. Last seen 2 years 20 weeks ago. Offline
Joined: 09/14/2009

I switched to cloudmade and the problem seems resolved. I guess it is openstreetmap that is just not responding at times.

jaak
User offline. Last seen 1 day 10 hours ago. Offline
Joined: 06/19/2008

Yes, OSM tile server is not really meant or suitable for commercial services. It has been unstable, it can be down for couple of days. See also http://wiki.openstreetmap.org/wiki/Tile_usage_policy

/JaakL

Kallin
User offline. Last seen 2 years 20 weeks ago. Offline
Joined: 09/14/2009

I take that back, it's not really solved using cloudmade. I am still having the problem where it there are no tiles coming in for minutes on end.

I've tried using the cloudmade server directly in my browser like http://b.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/7056/256/15/17600/10746.png and the most it ever takes is a few seconds. Is there a way to force the downloader to recognize timeouts?

jaak
User offline. Last seen 1 day 10 hours ago. Offline
Joined: 06/19/2008

What is your connection mode (MDS, direct TCP, Wifi) ? According to http://www.blackberry.com/knowledgecenterpublic/livelink.exe/fetch/2000/348583/800451/800563/How_To_-_Control_the_connection_timeout_for_TCP_connections_through_BlackBerry_Mobile_Data_System_Connection_Service.html?nodeid=1235131&vernum=0 I would suggest to try something like that:
map.setDownloadStreamOpener(new DefaultDownloadStreamOpener(";ConnectionTimeout=2000;deviceside=false"));

Technically you can also implement your own DownloadStreamOpener, but it is quite complex (HTTP forwardings etc) and it bases on HTTPConnection, not StreamConnection.

/JaakL

Kallin
User offline. Last seen 2 years 20 weeks ago. Offline
Joined: 09/14/2009

I'm running in the simulator, so technically the connection is broadband. I have been using ';deviceside=true'. I tried your suggestion of setting it false but it doesn't work as I don't have an MDS server running. I'd prefer to use direct TCP anyways as it is much more straightforward.

The 'freezing' does seem to last right around 2 minutes, which makes me think it must be timing out. that article also says: "Only TCP connections, made through the BlackBerry MDS Connection Service, support this parameter."

I'd really like to be able to just change the call to 'Connector.open' to be :
streamConnection = (StreamConnection) Connector.open(url.toString() + ";deviceside=true", Connector.READ_WRITE, true);

Is it possible to do that without writing my own streamer?

jaak
User offline. Last seen 1 day 10 hours ago. Offline
Joined: 06/19/2008

The issue here is that library uses HTTPConnection, not StreamConnection. If you want to use StreamConnection, then it probably needs not only new ConnectionOpener, but it could need to rewrite other parts of downloader, and currently there is no public API for it.

However, for modified DefaultDownloadStreamOpener.java you can use existing one as template, maybe some basic changes here would work for you:

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Hashtable;

import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;

import com.nutiteq.log.Log;
import com.nutiteq.utils.IOUtils;

/**
 * Default stream opener used inside library. Handles cleanup for resources
 * opened by it.
 * 
 * Status codes 200 (OK) and 304 (not modified) are handled the same way - with
 * data read.
 * 
 * This implementation tries to follow up to 3 redirects (HTTP status codes 301,
 * 302, 307). If it is not successful, then an error notification will be sent
 * to stream waiter.
 */
public class DefaultDownloadStreamOpener implements DownloadStreamOpener {
  private static final String HTTP_REDIRECT_LOCATION_HEADER = "Location";
  private final String urlExtension;
  private final Hashtable properties = new Hashtable();
  private static final int MAX_FOLLOWED_REDIRECTS = 3;

  /**
   * @param urlExtension
   *          Optional extension for http URLs, for instance ";deviceside=true".
   *          Used on some Blackberry devices.
   */
  public DefaultDownloadStreamOpener(final String urlExtension) {
    this.urlExtension = urlExtension;
  }

  public DefaultDownloadStreamOpener() {
    urlExtension = "";
  }

  /**
   * Add request properties, that will be added added to every request (for
   * example User-Agent).
   * 
   * @param propertyName
   *          request property name
   * @param propertyValue
   *          request property value
   */
  public void addRequestProperty(final String propertyName, final String propertyValue) {
    properties.put(propertyName, propertyValue);
  }

  public void openInputStream(final DownloadStreamWaiter streamWaiter, final String url) {
    openInputStream(streamWaiter, url, 0, null);
  }

  public void openInputStream(final DownloadStreamWaiter streamWaiter,
      final DataPostingDownloadable postingDownloadable) {
    openInputStream(streamWaiter, postingDownloadable.getUrl(), 0, postingDownloadable);
  }

  private void openInputStream(final DownloadStreamWaiter streamWaiter, final String url,
      final int redirects, final DataPostingDownloadable downloadable) {
    final String downloadableUrl = url + urlExtension;
    final DataInputStream dis = null;
    HttpConnection connection = null;
    InputStream is = null;
    String redirectUrl = null;
    try {
      final long startTime = System.currentTimeMillis();
      Log.info("Downloading " + downloadableUrl);
      connection = (HttpConnection) Connector.open(downloadableUrl, Connector.READ_WRITE, true);
      connection.setRequestMethod(HttpConnection.GET);
      connection.setRequestProperty("Cache-Control", "No-Transform");
      if (properties.size() > 0) {
        final Enumeration keysEnum = properties.keys();
        while (keysEnum.hasMoreElements()) {
          final String key = (String) keysEnum.nextElement();
          final String value = (String) properties.get(key);
          connection.setRequestProperty(key, value);
        }
      }

      if (downloadable != null) {
        final byte[] dataBytes = downloadable.getPostContent().getBytes("iso-8859-1");
        connection.setRequestProperty("Content-Length", Integer.toString(dataBytes.length));
        connection.setRequestProperty("content-type", downloadable.getContentType());
        final OutputStream dos = connection.openOutputStream();
        dos.write(dataBytes);
      }

      is = connection.openInputStream();
      final int responseCode = connection.getResponseCode();
      Log.debug("Connection opened in " + (System.currentTimeMillis() - startTime));
      if (responseCode == HttpConnection.HTTP_OK
          || responseCode == HttpConnection.HTTP_NOT_MODIFIED) {
        final long processStart = System.currentTimeMillis();
        streamWaiter.streamOpened(is);
        Log.debug("Response read in " + (System.currentTimeMillis() - processStart));
      } else if (responseCode == HttpConnection.HTTP_TEMP_REDIRECT
          || responseCode == HttpConnection.HTTP_MOVED_PERM
          || responseCode == HttpConnection.HTTP_MOVED_TEMP) {
        redirectUrl = connection.getHeaderField(HTTP_REDIRECT_LOCATION_HEADER);
        Log.debug("Redirect to " + redirectUrl);
      } else {
        streamWaiter.error(RESPONCE_NOT_OK, "");
      }
    } catch (final IOException e) {
      Log.error("Downloader: " + e.getMessage());
      streamWaiter.error(NETWORK_ERROR, e.getMessage());
    } catch (final SecurityException e) {
      Log.error("Downloader security: " + e.getMessage());
      streamWaiter.error(SECURITY_EXCEPTION, e.getMessage());
    } finally {
      IOUtils.closeStream(dis);
      IOUtils.closeStream(is);
      IOUtils.closeConnection(connection);
    }

    if (redirects == MAX_FOLLOWED_REDIRECTS && redirectUrl != null) {
      streamWaiter.error(TOO_MANY_REDIRECTS, "Too manu redirects created!");
      return;
    }

    if (redirectUrl != null) {
      openInputStream(streamWaiter, redirectUrl, redirects + 1, downloadable);
    }
  }
}

/JaakL

jaak
User offline. Last seen 1 day 10 hours ago. Offline
Joined: 06/19/2008

The problem seems to be that larger downloads (over 40KB or so per tile) block sometimes reading in java.io.InputStream.read , until it is timed out. Timeout is 2 minutes. These blockings seems to be most likely Blackberry-specific. One, and in several reasons good, solution would be to avoid so big tiles. Compress them, use less colors (e.g. 16 indexed colors), this way I have usually tiles below 20 KB. Also 128-pixel tile may help, but this has also penalty: creating new http connections in BB could be quite slow.

/JaakL

jaak
User offline. Last seen 1 day 10 hours ago. Offline
Joined: 06/19/2008

Latest library pre-relase 1.0.2 (get it from nutiteq.com/beta/lib) has now configurable TCP read timeout for data reads. Set it to for example 15 seconds:


...
final DefaultDownloadStreamOpener opener = new DefaultDownloadStreamOpener(";deviceside=true",15000);
mapComponent.setDownloadStreamOpener(opener);
...

/JaakL