Two-Minute Tutorial 4 Part 3 with jQuery Google Maps Plugin Version 2

TMT4 P3 Save Markers using JSON and HTML5 Web Storage (localStorage) with jQuery Google Maps Plugin Version 2 (Version 3 is now available – read below)

November 26, 2011 This is the first version of this tutorial (earlier than PhoneGap 1.1.0 and jQuery Mobile RC2). The new version of the tutorial is here and uses the latest versions of AppLaud and jQuery Google Maps Plugin Version 3. To get the benefit of the latest support from PhoneGap and jQuery it is recommended to use the new tutorial.

This tutorial uses jQuery Google Maps Plugin Version 2.

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 v2 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.

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. The javascript code that saves data in localStorage was added, some var declarations were reordered, and a new function parameter was added for better functioning of the app.

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 tmt4part3.zip (differences: contents of tmt4p3.js, 2 file names with tmt4p2 become tmt4p3). 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.

Note: August 23, 2011: This tutorial uses the jQuery Google Maps Plugin Version 2. The next version of the plugin, Version 3 Alpha, is currently available. The next version of this tutorial using Version 3 will be available later this month.
1A. Method One: Create NEW AppLaud project for this tutorial: TMT4P3

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

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

  • Contents of the attached zip file tmt4part3.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
      • /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. As an advanced tutorial, 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.

  • Download the attached zip file tmt4part3.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.

At app launch, local storage is checked to see if any marker data is stored. Any saved markers are added to the map, 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. 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(), callback function. This code is in jQuery Mobile’s pagecreate event handler and thus guaranteed to run once at app launch.

// Add markers saved in localStorage
var rawData, markerId, markerData = {};
for (var i = 0 ; i < localStorage.length ; i++) {
rawData = localStorage.getItem(localStorage.key(i));
localStorage.removeItem(localStorage.key(i));

// Data must be parsed here as it was stringified before saving
markerData = JSON.parse(rawData);

markerId = addNewMarker(
new google.maps.LatLng(markerData.latitude, markerData.longitude),
‘blue’, markerData);

// Re-add stringified marker data with new key (new google marker id)
localStorage.setItem(“marker” + markerId, JSON.stringify(markerData));
}
// Center map on last marker
if (i !== 0) {
$(‘#map_canvas’).gmap(‘option’, ‘center’,
new google.maps.LatLng(markerData.latitude, markerData.longitude));
}
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 line of the 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.

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 next localStorage method is used to remove it: localStorage.removeItem(). True to its name, the removeItem() method removes the key/value pair from storage. The key used in the removeItem() call becomes non-existent.

The next line uses JSON.parse() to parse the raw data string in rawData. Now markerData can be used as a regular javascript object, making available the fields previously defined. The next line uses the stored latitude (markerData.latitude) and longitude (marketData.longitude) coordinates for the google.maps.LatLng creator. This google maps object and the remaining markerData are used in the aptly-named function addNewMarker() to add the marker to the map and put it in the text list of markers. Finally, the function addNewMarker() returns the new marker’s new id into markerId.

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. The next line of code re-stores the marker data using localStorage.setItem().

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, google maps returns the marker id.

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 comment in tmt4p3.js:

// Add marker saved in localStorage, already have geocoding data
The block of 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 here. 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 list, and can be accessed for editing from there.

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.