Cane en vol

Creative Commons License photo credit: Free114

If you followed part 2 of this tutorial, you have an appengine application that displays a Google map in your browser, and lets you click the map to add markers to it.
While extremely useful on long, cold Winter nights, you probably want more from your application. For example, you’d probably like to be able to save those markers somewhere, so you can share them with your users.

This part of the tutorial will focus on this task. When you finish here, you’ll be able to add markers to your map, give your marker a title and a description, and save it to the appengine’s datastore.

This is what your application’s main page is going to look like:

You’ll notice a few changes from the page you developed in part II. Mainly – the map is now half the size of the screen, and there’s something that looks suspiciously like a data-entry form on the right side.
You are absolutely correct. The map on the left half of the page is the same map – only smaller in size, and I’ve added a form on the right to capture information about the markers you place on it.

First thing to do is to change the jsp file (DuckMain.jsp) to resemble the changes in the screenshot:

<%@ page contentType="text/html;charset=UTF-8" language="java"%>
<!DOCTYPE html>
<html>
<head>
<title>Mapping ducks</title>
<link rel="stylesheet" type="text/css" href="css/main.css" />
<meta charset="utf-8">
<script type="text/javascript"
    src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
            var map;
            var initialLocation = new google.maps.LatLng(-44.6895642,169.1320571);
            function init() {
                var duckOptions = {
                    zoom: 12,
                    center: initialLocation,
                    mapTypeId: google.maps.MapTypeId.HYBRID
                };
                map = new google.maps.Map(document.getElementById("map_canvas"), duckOptions);
                var marker = new google.maps.Marker({
                    position: initialLocation,
                    map: map
                });
                google.maps.event.addListener(map, 'click', function(event) {
                   placeMarker(event.latLng);
                });
            }
            function placeMarker(location) {
                    var marker = new google.maps.Marker({
                    position: location,
                    map: map
                });
                map.setCenter(location);
            }
        </script>
</head>
<body onload="init()">
    <h1>Mapping Ducks</h1>
    <div id="map_canvas"></div>
    <div id="marker_data">
        <form id="createForm" action="/new" method="post" accept-charset="utf-8">
            <table>
                <tr>
                    <td>Title</td>
                    <td><input type="text" name="title" id="title" size="66"/></td>
                </tr>
                <tr>
                    <td>Description</td>
                    <td><textarea rows="4" cols="50" name="description" id="description"></textarea>
                </tr>
                <tr>
                    <td>Latitude</td>
                    <td><input type="text" name="latitude" id="latitude" size="66" /></td>
                </tr>
                <tr>
                    <td>Longitude</td>
                    <td><input type="text" name="longitude" id="longitude" size="66" /></td>
                </tr>
                <tr>
                    <td>Address</td>
                    <td><input type="text" name="longitude" id="longitude" size="66" /></td>
                </tr>
            </table><br/><br/>
            <input type="submit" value="Save"/>
        </form>
    </div>
</body>
</html>

There are two main things to note here. First – I’ve added a CSS file:

<link rel="stylesheet" type="text/css" href="css/main.css" />

This is not strictly necessary, but it’s good practice. the href is relative to your WAR’s file root, and now would be a good time to create this css file.
Under your war directory in Eclipse, create a css folder, and inside create a new file and name it main.css.

#map_canvas {
    width: 650px;
    height: 450px;
    margin-left: 20px;
    float: left;
    clear: both;
}

#marker_data {
    width: 450px;
    height: 450px;
    float: left;
    margin-left:20px;
}
input, textarea{border:1px solid black}

Granted, this is a very simple stylesheet, but you are free to play with it and change it as you like.
The #map_canvas style changes the size of the map so that it doesn’t take up the entire paeg anymore, and let’s you add the form on the right.

The following code inside DuckMain.jsp takes care of displaying the form itself:

<div id="marker_data">
    <form id="createForm" action="/new" method="post" accept-charset="utf-8">
        <table>
        <tr>
            <td>Title</td>
            <td><input type="text" name="title" id="title" size="66"/></td>
        </tr>
        <tr>
        <td>Description</td>
            <td><textarea rows="4" cols="50" name="description" id="description"></textarea>
        </tr>
        <tr>
        <td>Latitude</td>
        <td><input type="text" name="latitude" id="latitude" size="66" /></td>
        </tr>
        <tr>
        <td>Longitude</td>
            <td><input type="text" name="longitude" id="longitude" size="66" /></td>
        </tr>
        <tr>
        <td>Address</td>
            <td><input type="text" name="longitude" id="longitude" size="66" /></td>
        </tr>
     </table>
     <br/><br/>
     <input type="submit" value="Save"/>
    </form>
</div>

This form doesn’t do anything yet, but that’s alright. All you care about right now is that your application’s main page displays something similar to the screenshot above.
Right-click your project and run-as a Google web application to verify that it works on your http://localhost:8888/ (or whatever port you use).

Populating the form

Now that you have your page set up nicely, you can move on to the next task – populating the form. Specifically, what you want to do is get the application to auto-populate the longitude and latitude fields when you add a marker to the map.

Why would you want to do that? Well – that’s the best way to save a marker’s data to the data store, and it’s not something you should expect your users to provide themselves. Additionally, just to be nice (and give your user some sort of feedback that they placed the marker at the right spot on your map) – we’ll auto-populate the address field as well, with the address corresponding to the marker just placed.

Doing all this is actually quite simple. There are two changes to make. One to the init() function, and one to the placeMarker() function.

//inside DuckMain.jsp
.
.
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script> //1
<script type="text/javascript">
    var map;
    var geocoder; //2a
    var initialLocation = new google.maps.LatLng(-44.6895642,169.1320571);
    function init() {
        geocoder = new google.maps.Geocoder(); //2b
        var duckOptions = {
            zoom: 12,
            center: initialLocation,
            mapTypeId: google.maps.MapTypeId.HYBRID
        };
        map = new google.maps.Map(document.getElementById("map_canvas"), duckOptions);
        var marker = new google.maps.Marker({
            position: initialLocation,
            map: map
        });
        google.maps.event.addListener(map, 'click', function(event) {
            placeMarker(event.latLng);
        });
    }
   
    function placeMarker(location) {
        var marker = new google.maps.Marker({
            position: location,
            map: map
        });
        //3 - all code until
        $("#longitude").val(location.lng());  
        $("#latitude").val(location.lat());
        geocoder.geocode( { 'latLng': location}, function(results, status) {
            if (status == google.maps.GeocoderStatus.OK) {
                $("#gaddress").val(results[0].formatted_address);
            } else {
                alert("Geocode was not successful for the following reason: " + status);
            }
        });
        //the end of (3)
        map.setCenter(location);
    }
</script>

Let’s look at the changes introduced here.

  1. Adding jQuery
    This line loads jQuery on your page. While not completely necessary, using jQuery allows us to dynamically modify the input fields in the form and populate them with the details of the newly added marker. This is further explained in (3) below.
  2. Geocoder is a Google Maps API v3 class that lets you find a physical address corresponding to a LatLng (latitude and Longitude) object. 2b is where you initialize it so you can later use it at point (3)
  3. Ok, this is where the magic happens.
    As you may recall from part 2 of this tutorial, the placeMarker() function is called when the user clicks the map to add a location-marker on it.
    While inside this function, the position of the marker is known. It is passed as the location argument to the function.
    The following lines use jQuery to write the values of the location’s latitude and longitude values into the fields on the form.

    $("#longitude").val(location.lng());
    $("#latitude").val(location.lat());

    They do this by locating the HTML elements with the ids of longitude and latitude and changing their values to be the longitude and latitude values from the location argument respectively.
    These ids correspond to the longitude and latitude input elements in the form.

    This leaves us with the Geocoder code:

    geocoder.geocode( { 'latLng': location}, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
            $("#markerAddress").val(results[0].formatted_address);
        } else {
            alert("Geocode was not successful for the following reason: " + status);
        }
    });

    This piece of code uses the Geocoder mentioned before, to find the physical address corresponding to the latitude and longitude of the marker just placed on the map.
    Geocoder’s geocode method takes the location argument (which is a LatLng object), and a function definition. The function is called by the broswer on Geocode’s return from service, and takes two arguments: the results (which is an array) and a status code.
    Inside the function, it’s a good idea to first check the status code. If Geocode succeeded, you’ll get an OK status. In this case, using jQuery again, the function places the formatted_address returned into the input field whose id is markerAddress.
    If for some reason Geocoder failed to return an address – an alert is displayed to the user.

So far so good?

It is time to test the application, to ensure that all is working correctly.
Run your application again, and navigate to http://localhost:8888 (or wherever it is your application gets deployed to). It’s probably a good idea to zoom the map in a little bit, but you don’t absolutely have to do it.
Click somewhere on the map, and if everything works as expected, you should have a new marker added, and the three form fields (latitude, longitude and address) should get populated with the data corresponding to your marker. For example – say you placed your marker on 56 Brownston Street, Wanaka (the location of the fantastic Matterhorn South in New Zealand). You should get something similar to the following screenshot:

Saving data to the data store

Placing markers on the map and finding their location and address is all very well. But you probably want to do a little bit more with the markers you place. This is where the appengine’s datastore comes into the game.
This post is not about the datastore, and there’s a lot to write about it. For the purpose of this simple application, it’s enough to show how to easily save the markers data there.

I’ll use the JPA flavor of access, just because it is quite straight-forward. There are only a few files you need to add to the application in order to persist and read your data.

There are a lot of sources on the web for how to do this, I personally like Lars Vogel’s blog, and used his suggestions here.

First – create persistence.xml under src/META-INF in your project:

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
       http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">

 
    <persistence-unit name="transactions-optional">
        <provider>org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider</provider>
        <properties>
            <property name="datanucleus.NontransactionalRead" value="true"/>
            <property name="datanucleus.NontransactionalWrite" value="true"/>
            <property name="datanucleus.ConnectionURL" value="appengine"/>
        </properties>
    </persistence-unit>
</persistence>

This will tell appengine to use JPA for your application. Note – when using the local version of appengine (that is – when you deploy your application locally rather than to appengine itself) – your data store is found in your Eclipse’s workspace, under WEB-INF, in a folder called appengine-generated. This is good to know, because when you make changes to your models and nothing works anymore – you need to remember to delete the files under this folder, to let your local appengine environment re-generate the data store with the new models structure.

After you sorted persistence.xml out, you need to add a way to hook into the EntityManager, and a model too:

EntityManagerService

package com.duckranger.maps;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class EntityManagerService {
    private static final EntityManagerFactory emfInstance = Persistence
            .createEntityManagerFactory("transactions-optional");

    private EntityManagerService() {
    }

    public static EntityManagerFactory get() {
        return emfInstance;
    }
}

The EntityManagerService class is a simple Singleton, that lets you (via the get() method) acquire a handle to an EntityManagerFactory. The factory will be used later to getn EntityManager instance – where all the good things happen (well, at least as far as the data store is concerned).

Now – the model. We’ll name our model class Duck, as the application is really about spotting ducks in the wild (if you haven’t figured it out yet…)

Duck.java

package com.duckranger.maps;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Duck {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long duckId;
   
    private double longitude;
    private double latitude;
    private String description;
    private String address;
    private String title;
 
    //I have ommitted the getters and setters - you have to have them, though. 
}

There’s not much to this class. It’s only worth noting that the @Entity annotation tells the appengine that this class is an entity worth persisting, and the duckId plus its annotations – which designate this field as the primary key for the Duck entity, and tell appengine to generate it itself.

To actually save your ducks (or, the markers we place on the map…) – you’ll need to create one more class: a servlet that listens for the form-submission on DuckMain.jsp, and persists the data to the data store.

NewDuck servlet

package com.duckranger.maps.servlet;

import java.io.IOException;

import javax.persistence.EntityManager;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.duckranger.maps.Duck;
import com.duckranger.maps.EntityManagerService;

@SuppressWarnings("serial")
public class NewDuck extends HttpServlet {

    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {

        EntityManager entityManager = EntityManagerService.get().createEntityManager();
        Duck duck = new Duck();
        duck.setTitle(req.getParameter("title"));
        duck.setDescription(req.getParameter("description"));
        duck.setLatitude(Double.parseDouble(req.getParameter("latitude")));
        duck.setLongitude(Double.parseDouble(req.getParameter("longitude")));
        duck.setAddress(req.getParameter("address"));
        try {
            entityManager.persist(duck);
        } finally {
            entityManager.close();
        }
        resp.sendRedirect("/DuckMain.jsp");
    }
}

As promised, this is a very simple servlet, which only has the doPost() method. This is important, because we only expect the servlet to receive an HTTP POST method (via the form submission on DuckMain.jsp)

The servlet acquires an EntityManager instance using our EntityManagerService class. It then creates a new Duck object, and sets its values based on the request parameters it received. (Note that there’s no validation made on these parameters, you are welcome to add it if you want).

After the new Duck is ready, the servlet tries to save it to the datastore using EntityManager’s persist() method. After all is done – the servlet redirects the user back to DuckMain.jsp.

There’s only one more thing to do in order to tie it all together, and that’s mapping the NewDuck servlet in web.xml.

<!-- These are snippets inside web.xml -->
<servlet>
    <servlet-name>NewDuck</servlet-name>
    <servlet-class>com.duckranger.maps.servlet.NewDuck</servlet-class>
</servlet>
.
.
<servlet-mapping>
    <servlet-name>NewDuck</servlet-name>
    <url-pattern>/new</url-pattern>
</servlet-mapping>

The first snippet you add to web.xml define a new servlet in the system: NewDuck. Note the servlet-class definition – it may be different for your code if you used a different package.

The second snippet maps the NewDuck servlet to serve the /new URL requests. If you look back at DuckMain.jsp, you’ll see the following line:

<form id="createForm" action="/new" method="post"
   accept-charset="utf-8">

This defines the target of the form (that is – the URL it will try to submit to) as /new. Working together, web.xml and DuckMain.jsp tell appengine that when the form on DuckMain.jsp is submitted, it should send the HTTP POST request to the NewDuck servlet.

Verify that it works

Try and redeploy your application. Before the last batch of changes, you saw that when you add a marker on the map, the application populates the latitude, longitude and address fields. Do it now, and add a title and description. Click on Save, and if everything works – you should be redirected to the (empty, and centered on the Wanaka Bakpaka) DuckMain.jsp page.

Seeing your saved ducks

If you did this, then it probably all works, but you’re wondering – did my application really save the marker’s data?
Well, let’s verify that!

To verify that your ducks are saved, you’ll add one more servlet to your application – a list servlet:

ListDucks servlet

package com.duckranger.maps.servlet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.duckranger.maps.Duck;
import com.duckranger.maps.EntityManagerService;

@SuppressWarnings("serial")
public class ListDucks extends HttpServlet {
   
    @SuppressWarnings("unchecked")
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {

        EntityManager entityManager = EntityManagerService.get().createEntityManager();
        List<Duck> ducks = null;
       
        try {
            Query duckQuery = entityManager.createQuery("select d from Duck d");
            ducks = new ArrayList<Duck>(duckQuery.getResultList());
        } finally {
            entityManager.close();
        }
       
        if (ducks != null & !ducks.isEmpty()) {
            resp.getWriter().println("Ducks:\n");
            for (Duck d : ducks) {
                resp.getWriter().println("Duck: "+ d.getTitle()+ " at "+ d.getAddress());
            }
        } else {
            resp.getWriter().println("No ducks yet.");
        }
    }
}

Quite the simple servlet this one.

Note that, unlike what you did in the NewDuck servlet, in ListDucks you are providing a doGet() method. This makes sense, because this servlet only lists data – it doesn’t create or change data on the server.

Inside the try block, the servlet tries to retrieve all Ducks from the data store into a local ArrayList object.
After that, if the query returned actual Ducks – the servlet iterates over the Duck objects, and prints out the title and address the user entered for each Duck.

To see this servlet in action, you’ll need to go back to web.xml and register it with the application:

<!-- snippets in web.xml -->
.
.
<servlet>
    <servlet-name>ListDucks</servlet-name>
    <servlet-class>com.duckranger.maps.servlet.ListDucks</servlet-class>
</servlet>
.
.
<servlet-mapping>
    <servlet-name>ListDucks</servlet-name>
    <url-pattern>/list</url-pattern>
</servlet-mapping>
.
.

As before – you define the servlet itself, and give it a servlet mapping. Unlike the case of the NewDuck sevlet, the url-pattern for this servlet-mapping does not depend on anything. I chose to define it as /list but you can change it to a different mapping if you like.

When you redeploy your application and point your browser at http://localhost:8888/list – you should see the ducks that are already registered in your application. (Something like the following screenshot)

And that’s it for this part of the tutorial. In the next part – you’ll see how to load the Ducks from the data store so that you can display them on the map, and add those nice little baloons to them as well.

Popularity: 14% [?]

 

5 Responses to Google appengine and maps tutorial with Java – Part III

  1. Mark Silberbauer says:

    Thanks, very useful!

  2. jeremy says:

    Are you going to do part IIII I would be very interested to finish this great tutorial. Even just a post of the code would be awesome.

  3. Duck Ranger says:

    @jeremy – yes.. I’ve been a bit busy in the last few months, but will put it up soon.
    Cheers

  4. Mark Morris says:

    Good Stuff!

    Need to correct the linkage between the form, jquery, and parameter (I made them all “gaddress” …

    DuckMain.jsp

    if (status == google.maps.GeocoderStatus.OK) {
    $(“#gaddress”).val(results[0].formatted_address);

    input type=”text” name=”gaddress” id=”gaddress” size=”66″

    NewDuck.java

    duck.setAddress(req.getParameter(“gaddress”));

  5. Rahul says:

    nice….can you give tutorials on JDO it will be helpful….

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Set your Twitter account name in your settings to use the TwitterBar Section.