Two-Minute Tutorial 4 Part 3

TMT4 P3 Save Markers using JSON and HTML5 Web Storage (localStorage)

Part 3 of the advanced Google Map tutorials builds on Part 2 by using HTML5 Web Storage to save the marker data on the device, allowing markers to be saved across app launches. This tutorial utilizes the persistent local storage feature available in HTML5 to store marker data and associated information in JSON format.

Sample App Overview

The previous tutorial, TMT4P2 : Dynamic Markers, Geocoding and jQuery Google Maps Plugin, demonstrated two ways to dynamically add markers to a Google Map, using the jQuery Google Maps Plugin v3 and jQuery Mobile. The marker data was stored only in HTML and subsequently lost each time the app’s HTML was reloaded. This tutorial adds approximately 45 lines of javascript code to TMT4P2 to enable persistent markers on a google map. (The tutorial for the earlier jQuery Google Maps Plugin Version 2 tutorial is here)

With the popularity of HTML5, there is an abundance of information about Web Storage, including w3schools.com’s documentation on localStorage and sessionStorage. TMT4P3 tutorial uses HTML5 localStorage, not sessionStorage, to save marker data.

Additional reading: Dive Into HTML5 by Mark Pilgrim, a well-written and concise description of localStorage. The chapter on Using HTML5 Storage was particularly helpful for this tutorial as it contains many useful javascript code snippets and explanations. To learn about JSON format start here.

Prep

Familiarity with the previous tutorial TMT4P2 : Dynamic Markers, Geocoding and jQuery Google Maps Plugin is recommended. The code in this tutorial is a superset of TMT4P2.

Use one of the two methods described below (1A or 1B) to create an AppLaud project containing the source code for this tutorial. In the first method a new project is created in the identical manner to TMT4P2, but using the source in tmt4p3.zip. The second method re-uses the project from TMT4P2 and substitutes the new javascript file tmt4p3.js for tmt4p2.js.

For information on getting started with AppLaud, see installation instructions on the Get Started page.

1A. Method One: Create NEW AppLaud project for this tutorial: TMT4 Part 3

Download the attached zip file, tmt4p3.zip. Create a new project following the instructions from TMT4P2 : Dynamic Markers, Geocoding and jQuery Google Maps Plugin, using the tmt4p3.zip files instead of the tmt4part2.zip files. Remember to make the additions to index.html as described in those instructions.

Note: Review file references in index.html carefully. The file names using “tmt4p2” now use “tmt4p3”.

  • Contents of the attached zip file tmt4p3.zip
    • /src directory contains:
      • index.html – defines app’s two pages using jQuery Mobile multi-page markup
      • tmt4p3.js – all javascript for app (tmt4p2.js + new code for localStorage)
      • tmt4p3.css – important styling for map display and app
      • markerwithlabel.v1.1.5.min.js – to add labels to markers
      • /images/markers – images for custom marker icons and map controls
    • /jquery.ui.map contains:
      • jquery.ui.map.min.js – minified, version 2 plugin (use for market version)
      • jquery.ui.map.js – non-minified, version 2 plugin (use for development)
      • jquery.ui.map.extensions.js – custom extension of maps plugin

1B. Method Two: Re-Use the AppLaud Project Containing TMT4P2

The following instructions describe substitution of tmt4p3.js for tmt4p2.js. It is assumed the reader will do this task as described below, or in a manner which accomplishes the same end result: the code in tmt4p3.js is used in place of the code in tmt4p2.js in an existing TMT4P2 project.

  • Download the attached zip file tmt4p3.zip and locate the file tmt4p3.js:
    • /src/tmt4p3.js – all javascript for app (tmt4p2.js + new code for localStorage)

Import tmt4p3.js into the project’s directory assets/www/.

Rename the file tmt4p2.js to tmt4p2.js.hold.

Rename the file tmt4p3.js to tmt4p2.js.

Refresh the project if file was imported outside Eclipse.

2. Discussion of Marker Data Save / Remove using Local Storage

Description of Marker Save / Remove Logic

There is no state regarding saved or removed markers: if the user puts a marker on the map, it is immediately saved in local storage. If the user removes a marker, it is immediately removed from local storage. When the app exits there is nothing needs to be saved.

At app launch, the device’s local storage is checked to see if any marker data is stored. Any saved markers are added to the map using the same code as dynamically added markers (‘tap’ event), and remain in local storage. The “run time” of the app allows the user to add, edit or delete markers.

As web storage uses a key / value system, one key (unique) is associated with one value (of any type). When a marker is added to a google map, a unique marker id is generated by google maps. The marker id is used by the app to create a unique key for that marker’s data. Each marker’s associated data (lat/lang, title, address, etc) is saved until the user explicitly deletes it. The marker data is stored in a javascript object, JSON format. The JSON data is stringified for saving as the value associated with the key.

Read Marker Data from Local Storage at App Launch

The following block of code was added to the map creation call, $(‘#map_canvas’).gmap(), init function (callback after map creation). This code is in jQuery Mobile’s pageinit event handler for page-map and thus guaranteed to run once at app launch.

        // Read saved markers from localStorage
        var rawData, markerArray = [], length = localStorage.length, i;
        var markerCount = 0;
        for (i = 0 ; i < length ; i++) {
            var key = localStorage.key(i);
            if (key.indexOf(“marker”) === 0) {
                markerCount++;
                rawData = localStorage.getItem(localStorage.key(i));
                // Data parsed here as it was stringified before saving
                markerArray[i] = JSON.parse(rawData);
            }
        }
        // Remove all ‘old’ markers from localStorage
        localStorage.clear();
        // ATTN: Remove items individually if app saves other data!
        // Add markers with new markerID to map and localStorage
        for (i = 0 ; i < markerCount ; i++) {
            addNewMarker(
                    new google.maps.LatLng(markerArray[i].latitude, markerArray[i].longitude),
                    ‘blue’, markerArray[i]);
        }

The first use of localStorage is to access its length property to control the for loop. The length property is the total number of values in the storage area for this app. If the length is zero, no data has been saved, and the code in the for loop is not executed. This app saves one key/value pair for each saved marker, and doesn’t save any other data, thus the localStorage.length value is also the number of saved markers.

The first for loop uses the current index, i, to fetch each key stored using the method localStorage.key(). Using this method of iterating through values in localStorage, prior knowledge of a key name is not needed. Only the value (marker data) is needed, not the key value, so the string returned by .key() is not saved. As we saved each marker with the string ‘marker’ prepended to the key, we check for that string at the beginning of each key.

The next method used, localStorage.getItem(), takes a key and returns the associated value. The value is an array (or string) of marker data in this case. If the key is non-existent, a null value will be returned.

The marker data is saved in rawData. This data can no longer be associated with the current key, as google map marker ids change with each load. The marker data is parsed with JSON.parse() and saved in an array.

The method localStorage.clear() is called next to remove all the saved markers. All the marker data has been read, parsed saved in markerArray.

Now markerArray can be used as an array of regular javascript objects, making available the fields previously defined. The next for loop uses the stored latitude (markerArray[i].latitude) and longitude (marketArray[i].longitude) coordinates for the google.maps.LatLng creator. This google maps object and markerArray[i] data are used in the aptly-named function addNewMarker(). This function adds the marker to the map, put it in the text list of markers list, and saves the new marker data (using freshly gotten google map marker ID) back into local storage.

The app identifies each marker by its google map-assigned id. A marker’s id changes each time it is added to a map, so we must store the marker data using its new (current) marker id. The marker’s stored key was generated using the google map’s previous instance of the marker and is therefore stale.

Using the marker’s new id to generate the key guarantees that 1) it will be unique, and that 2) we will be able to easily find and delete the marker in the current instance of the google map. When the user creates a marker or taps on an existing marker, the google maps plugin returns the marker id for use in a callback, e.g. in a tap event callback.

Add Marker Data from Local Storage at User’s Request

The following code was added to the marker dialog’s save handler (user clicks Save button in marker dialog).

        // Put marker data into object and save in localStorage
var markerData = {};
markerData.latitude = marker.getPosition().lat().toString();
markerData.longitude = marker.getPosition().lng().toString();
markerData.id = $(‘#tag’ + markerId).val();
markerData.address = $(‘#address’ + markerId).val();
markerData.state = $(‘#state’ + markerId).val();
markerData.country = $(‘#country’ + markerId).val();
markerData.comment = $(‘#comment’+ markerId).val();

localStorage.setItem(“marker” + markerId, JSON.stringify(markerData));

After adding the marker data into the markerData object with easily identifiable field names, the key for the marker is generated by adding the string “marker” to the unique id (integer) in markerId. This key and the value in markerData are stored using localStorage.setItem().

Note: If the user edits an existing marker and (re)saves it, key is the same key used when the data was first saved. Calling the method .setItem() with an existing key will silently overwrite the existing data. In this case – a very simple data model –  this happens to be exactly what is needed. Other uses of .setItem() may need to be used with caution.

Remove Marker Data from Local Storage at User’s Request

The following line of code was added to the marker dialog’s remove handler (user clicks Remove button in marker dialog).

localStorage.removeItem(“marker” + markerId);

We know the marker data was saved with a key created by adding the string “marker” to the unique google map marker id. That key is easily re-generated with the above code and passed to removeItem().

Add Saved Marker Data to HTML Data Structure and Marker List

Search for the following in tmt4p3.js:

     } else {
localStorage.setItem(“marker” + markerId, JSON.stringify(savedMarker));

// Add marker saved in localStorage, already have geocoding data

The code following it was added to the function addNewMarker() to maintain the marker data in the html structures and the ul marker list, and will not be discussed in detail here. This block of code is run when a marker is added from local storage at app start up (as opposed to added by a user tap event). The saved marker data is not presented to the user in a dialog as new marker data is; the information is put straight into the structures and text marker list, and (re-)saved in locale storage with its new marker id.

Using HTML5 localStorage

The localStorage API is relatively straightforward and meets the needs of marker data storage. The tutorial takes advantage of the simple structure of marker data and the unique marker id provided by google maps. More complex data may require a proper data management layer.

Share your results on the discussion mailing list.