Archive

Posts Tagged ‘web service’

Building database and web service driven asynchronous google map application

May 22nd, 2007

Map API could help build very cool mashup websites.  Often times a back end database is the source of the objects to be drawn on the map and querying for those objects from the database based on the parameters the users choose on the website (e.g., state, city, search criteria) gets complex.  Also to improve the user experience, asynchronous map loading is desired so that while the next batch of objects is being retrieved from the server, the current ones on the map could continue functioning.  To address all these challenges and build a smooth database driven asynchronous loading map application, web service can serve as the bridge between the server side complex data model and the client side map drawing logic.  The following describes the steps of implementing such architecture and a sample code project created in Visual Studio (free version could be downloaded here).  This time, I played with Google Maps API.

ws_map.jpg

Firstly, set up a basic ASP.NET AJAX-enabled web site through the Visual Studio wizard.  You may need to download the AJAX extension from http://ajax.asp.net/.  Inside Default.aspx, add a div in the body like the following to host the map

<div id="map" style="width: 90%; height: 600px; margin-left:auto; margin-right:auto"></div>

Reference the google api javascript source and your own javascript file (e.g., gmap.js where the client side mapping code is located) in the head section.

Next is to prepare the data on the server side.  It is actually much easier to set up a relational database in Visual Studio by a just few clicks and there are tons of such examples on the web.  So here let’s try a slightly trickier scenario where we only have a static plain text CSV data file (e.g., exported from an legacy system or Excel spreadsheet) that contains various cities and the fun places in them to be drawn on the map.  Add that data file (e.g., mapdata.csv) under the App_Data/ folder in the solution explore.  Assume the data file has 3 columns State, City, and FunPlace.  Now create a web service (e.g., MapDataWebService) to extract the data from the CSV file and serve to the client side mapping code.  To add this web service, right click the project in the solution explore, select "Add New Item…", select "Web Service", and then rename the file name into "MapDataWebService.asmx".  The web service skeleton is automatically added under App_Code/.  Open it and add 2 web methods getAllStates() and getFunPlacesByState(string state).  The former is used to populate the dropdown on the web page to select a state and the latter is to get the fun places of the selected state.  Both methods connect to the data file through OLE DB and query the data using convenient SQL syntax.  To quickly unit test the web method, you could open MapDataWebService.asmx and press F5 to run the web service alone.

The return value of getFunPlacesByState(string state) would be an array of objects (type of class Place), each of which contains the latitude and longitude of the city and the name of the fun place in that city.  The class Place is the object oriented representation of this information.  The web service will return the objects to the JavaScript caller.  In order for the caller to get the consistent objects and dereference them the same way as in C#, the following directive should be added to the top of the web service code and a reference to the assembly System.Web.Extensions.dll needs to be added to the solution too.  If you don’t have this assembly already installed on your machine, you could download and install "ASP.NET Ajax" from http://ajax.asp.net/.


using System.Web.Script.Services;

    [ScriptService]
    [GenerateScriptType(typeof(Place))]
    public class MapDataWebService : System.Web.Services.WebService
    {
….
    }
    public class Place
    {
….
    }

After the data service is created, the getAllStates() web method could be used to build a data source to populate the dropdown list.  Drag and drop a dropdown list onto the Default.aspx, select to create a new data source from its context menu, choose object as the type, MapDataWebService as the business object, and getAllStates() as the SELECT method.  The data binding is automatically done.  Add an onchange event handler to this control to trigger the mapping function (e.g., drawMarkers) and we are almost done.

The last step is to implement the mapping method drawMarkers() in gmap.js.  It retrieves the state that the user chose in the dropdown and use it as the parameter to call the web service method getFunPlacesByState(state) asynchronously.  The callback function will receive the resulting list of Place objects and then use the Google maps API to draw them on the map.  To successfully make the web service call from JavaScript, it has to be registered in the Default.aspx like the following.

<asp:ScriptManager ID="ScriptManager1" runat="server">
    <Services>
        <asp:ServiceReference Path="~/MapDataWebService.asmx" />
    </Services>
</asp:ScriptManager>

To make it more fun and improve the user experience, during the async web service call, a spinning progress icon could be display indicating that the data is being loaded.  Overall, no whole page loading is done and web page looks better.  If you are interested the sample solution and code can be downloaded GMapExample.zip.  Before you can compile and run this web site, go to http://www.google.com/apis/maps/ to register your API key and replace the place holder "YOUR_API_KEY" in file Default.aspx and App_Code/MapDataWebService.cs.

By the way, a comment about Google Maps API.  The markers manager is a convenient way of aggregating all the markers, but removing objects from the manager seems buggy.  Whenever the map zoom level is changed, all the removed markers come back on the map.  The markers in the manager are not controlled by the map.removeOverlay() either.  Other than that, the API works pretty well.

Sample code download: GMapExample.zip

Web , , , , , , , , , ,

Live.com Zillow Gadget for Tracking Your Home Value

May 6th, 2007

I just created and published a new gadget on live.com called "Zillow Home Value Estimate".  Please give it a try!  The above link points to the gadget hosted on gallery.live.com.  If you are brave enough, you can also try the one I hosted on my own site by clicking the following link "Add the gadget to your live.com" or "Add the gadget to your live space".  I would normally upgrade the 2nd one quicker :)

Screenshot:

zillowGadget.jpg

If you are a home owner put this gadget on your own personalized live.com homepage, so you could closely monitor the value trend of your property.  Type in the street address and city/state (or zip code) of the real estate property that you are interested in, the zillow estimate (zestimate) and the 1 year value trend chart will appear on your live.com personalized home page.  To change the address, click on the underlined address itself and the editing functionality will show up.  The gadget uses zillow web services API to query the home value estimate.

A tip for developing live.com gadgets is beside publishing your new gadgets to the live.com gallery, you could also post a quick link on your own website/blog too so that the visitors can add your gadgets to their home page by a simple click on your site.  All you need to do is put a hyper link http://www.live.com/?add=URL_TO_YOUR_GADGET_XML_FILE for adding to live.com and http://spaces.live.com/spacesapi.aspx?wx_action=create&wx_url=URL_TO_YOUR_GADGET_XML_FILE for adding to live spaces. For example: <a href="http://www.live.com/?add=http://stanley.jojoyao.com/live_gadgets/Zillow/ZillowGadget.xml">Add to Live.com</a> and <a href="http://spaces.live.com/spacesapi.aspx?wx_action=create&wx_url=http://stanley.jojoyao.com/live_gadgets/Zillow/gadget.xml">Add to Live Spaces</a>

Web , , ,

Display multiple virtual earth maps on the same web page

March 12th, 2007

Playing with mapping service API is a lot of fun, especially the Virtual Earth 3D maps, on which you can adjust altitude, heading, pitch and so on and pan through the city downtown over the skyscrapers.  Google map and Microsoft Virtual Earth has similar JavaScript/AJAX interfaces.  Tried the MTGoogleMaps plug-in and it’s quite good and easy to use.  There is one problem though when displaying, on IE, multiple maps by using multiple template tags on the same page.  The problem the tag will generate the same script-lets each time it appears on a page.  So there are multiple pairs of <div id="map"> and <script> in the resulting page, and the element ID of them are all the same – "map".  Although the element IDs are supposed to be unique across the page, but the browser will still try to render the page when there are ID collision.  However the result is not consistent on IE and Firefox.  Look at this example:

Element ID Collision
    <div id="Div1" style="width: 100px; height: 100px">d1</div>
<script type="text/javascript">
var v = document.getElementById("Div1");
alert(v.innerHTML);
</script>

    <div id="Div1" style="width: 100px; height: 100px">d2</div>
<script type="text/javascript">
var v = document.getElementById("Div1");
alert(v.innerHTML);
</script>

The above script-let will show "d1" and then "d2" on firefox, which suggests the space locality.  The closer element is picked when there are multiple elements with the same ID.  But on IE, "d1" will show up twice.

To fix this issue, firstly we need to move the <div> tag (hosting the map) generation into the client script so that we can generate tags with different IDs.  Secondly, use an array to keep track of all the map objects in case we need them later to further manipulate the maps.

Dynamic Map Object Generation without ID Collision
function CreateMap(width, height)
{
    // If there is no maps on this page at all, initilize the map array
    if (!maps)
    {
        maps = new Array();
        mapId = 0;
    }
   
    // Create a new map and append it to the array
    var nextIdx = maps.length;
    var nextDivId = "mapDiv" + nextIdx;
    document.writeln(‘<div id="’ + nextDivId + ‘" style="position:relative; width:’ + width + ‘; height:’ + height + ‘;"></div>’);
    var map = new VEMap(nextDivId);
    maps[nextIdx] = map;
    ++ nextIdx;
   
    return map;
}

function GetMapByCoordinates(latitude, longitude)
{
    // …
    map = CreateMap(width, height);
    map.LoadMap(new VELatLong(latitude, longitude));
    // …
}

Web , , ,