Tuesday, October 11, 2011

Map Tiles For Offline Usage Using ArcGIS API for Flex

So… Google introduced an offline feature to their mobile mapping application, enabling you to view map tiles when you are disconnected from the network. This is pretty neat and very useful now that local storage is so “abundant” on mobile devices. In this post, I would like to show you how to use the mobile device local storage for offline tile retrieval using the ArcGIS API for Flex. When we built the API, we always had the vision of extensibility to enable people to do things that we did not think about. One of then was to enable the control of the URL from where the tiles will be retrieved. A while back, I did such an implementation using Amazon S3. So, I rehashed that code using Adobe AIR File capabilities. The demo application that I am featuring here operates in two modes; an online mode and an offline mode. In the online mode, I keep a set all downloaded tiles for a particular viewing session. Before I go offline, I download the map server metadata and all the visited tiles to my device local storage. The AIR runtime can notify an application when the network connectivity changes. This enables me to put the application in offline mode and when I start panning and zooming, rather than retrieving the tiles from the cloud, I retrieve the tiles from my local storage. Pretty neat, eh ? So here is the code:
public class OfflineTiledMapServiceLayer extends ArcGISTiledMapServiceLayer
{
  override protected function getTileURL(
     level:Number,
     row:Number,
     col:Number
  ):URLRequest
  {
    var urlRequest:URLRequest;

    if (Model.instance.isOffline)
    {
      urlRequest = new URLRequest(
        "app-storage:/l" + level + "r" + row + "c" + col);
    }
    else
    {
      urlRequest = super.getTileURL(level, row, col);
      if (urlRequest.url in Model.instance.cacheItemDict === false)
      {
        const item:CacheItem = new CacheItem();
        item.urlRequest = urlRequest;
        item.level = level;
        item.row = row;
        item.col = col;
        Model.instance.cacheItemDict[urlRequest.url] = item;
      }
   }

   return urlRequest;
 }
}
The OfflineTiledMapServiceLayer extends the ArcGISTiledMapServiceLayer class and overrides the getTileURL function. This function is invoked to get the tile URL for a particular map level, row and column. If the application mode is offline, then the “app-storage” url scheme is used and the path is in the form of “l”+level+”r”+row+”c”+column. If the application mode is online, then the super.getTileURL is invoked and we keep a set of visited URLs. Using the application settings view, the application has the option to download the map server metadata and iterate over the visited tiles and save the bitmap images to the local storage as defined by File.applicationStorageDirectory. The AIR runtime has the capability to notify the application of a network change. When this occurs, I ping a URL (www.google.com) using the HTTPService to determine if this is a connect or a disconnect change thus putting the application in an online or offline state.
The application can be written in such a way that any visited tile can automatically be saved to the local storage, I leave that as an exercise for the reader :-)
Like usual, all the source code is available here.
NOTE: This sample application is for demonstration purposes ONLY and is intended to be used with your own legally cacheable tiles - I am not a lawyer, but I am pretty sure that it is not legal to save locally the ArcGIS.com accessible tiles.

8 comments:

Matt said...

I'm new to Flex and am trying to get this example to work, but there's no project file included with the source.

I tried creating my own in FlashBuilder 4.6 but the compiler won't recognize the ns:Registry tag in the application mxml file and it can't resolve com.esri.signal.Signal in the SettingsView.mxml file - also I can't find that namespace in agslib.

Any idea what I'm doing wrong? Dr. Google hasn't helped me out so far :-(

thunderhead said...

You need to add my MVC "Holistic" library - you can download it from http://dl.dropbox.com/u/2193160/HolisticLib2Test.fxp

Parks said...

Speaking of offline mapping, I'm working on a tablet flex app which takes advantage of a little of this code plus the local shapefile code from a few years ago to function in an offline capacity. It works well except for the slow drawing of the shapefiles (road/street layers). I saw a related blog (http://www.webmapsolutions.com) which mentioned using "fast layers" (and gave credit to you). This sounds good, except I don't know what "fast layers" are. Can you enlighten?

Thanks!

Douks said...

Hey Parks.

Any chance you would share that little app. I would welcome the opportunity to see this code integrated.

Thanks in advance!

Douks said...

Hi Parks and Mansour

Never mind. Got it figured out. I even added a hex converter so I can copy the entire cache without having to browse on line to get the data...it's better that way for us because tablets are deemed too insecure to connect to our network. I've put the files in a shared directory so I can manually copy them and the app reads them from there. I will do it for the Ipad next (that was for the playbook)....with the IPAD the Icloud could be a pretty good option...I could even push cache updates in there...but I take it it would be slow...Can't wait for our department to allow tablets on the network....will make my life way easier!

MANSOUR...you rock..see you at the dev summit in a few weeks...Je suis la personne de Parcs Canada en passant!

Prem said...

Great post, I did something similar to Douks for reading tiles from the tablet and then I saw your post today about the TPK layer which essentially is the compact cache format so that essentially opens the doors for the copying compact caches instead of exploded caches which is awesome coz it takes a while when a cache has a large number of files.

My question is actually partly related to this app and partly to the holistic implementation. Any particular reason why you chose the NetworkController to implement IMXMLObject and not just initialize it like any other class?

Robert Baluyot said...

Hi Mansour, I'm new to FLEX and trying to get your sample work but find no luck. There is no project file.

Please enlightened me on how this will work.

God Bless
Robert

Unknown said...

Hi Mansour,

I'm new in developing using ArcGIS API and I was wondering if it is possible to use this library with Flash. I have tried adding the swc and writing some base line of code but I get immediately error.

Can you please share an example of a simple Offline Mapping Application using Adobe AIR. I'm trying to develop one but I don't find any really helpful and complete example of this just a video and your post.

Sincerely,
Filippo