Added by Brent Owens, last edited by Brent Owens on May 04, 2006  (view change)

Labels:

Enter labels to add to this page:
Wait Image 
Looking for a label? Just start typing.

We now have a working map and a geographic search. Next we will make the search results a link that when clicked on, will zoom to their location on the map.

First, we need to have our results displayed in a scrollable text area that allows HTML in it (our links). Normal text areas do not allow HTML. So we can't use them. But we need a box that is scrollable so when we get 2000 results returned to us, the page doesn't become a mile long!

The solution to this is to make a text box out of a <div> tag and change the style so it will have scroll bars. This won't give the box a border, so I made a table with one cell and put the <div> box in that cell.
Here is the html code:

<table border=1 bordercolor="#7CCE4A">
    <tr>
        <td>
            <div style="overflow:auto; height:150px; width:430px;">
                <table cellpadding="0" cellspacing="0" style="width:410px;"><tr><td> Search results do in here </td></tr></table>
            </div>
        </td>
    </tr>
</table>

The <div> tag has its style set at a fixed width and height, and with the 'overflow' value set to "auto" (this causes it to scroll).

I put all of this HTML in another div tag, so I can turn the whole thing off and on (hiding it when the user isn't searching).
I then placed this div tag below my search form. The surrounding div tag looks like this:

<div id="search_result_gnis_span"> other table and div in here </div>

Initially, when the page loads, I made it so the html inside the div is empty. So when the user first loads the page, there is no search results box. This make the page less cluttered.
When the user searches for a geographic location, I need to enable the box (make appear) so I can add the search results to it.

I do this after I have received the search results back from the WFS server (GeoServer) and I am done parsing them. All I do is, in javascript, is locate the div tag I want

document.getElementById('search_result_gnis_span');

And then set the Inner HTML of the tag:

document.getElementById('search_result_gnis_span').innerHTML = writeSearchResultsHeader()+
' <b>'+fm_nodes.length+
'</b> results. <font size=-2><i>Search took '+duration+
' seconds.</i></font><br><table border=1 bordercolor="#7CCE4A"><tr><td><div style="overflow:auto; height:150px; width:430px;"><table cellpadding="0" cellspacing="0" style="width:410px;"><tr><td>'+
values+'</td></tr></table></div></td></tr></table>';

Recognise the getElement call? This one searches the current HTML document and gets the first element with the ID passed into the function.
All we did here was set the HTML inside the div tag.

Make the search results links

So now our results box can scroll and contain html (links) inside of it. Sweet.
Lets go back to where we parse the search results, and have it output a long string of html links.

fc_node = getElements(geo_xmlhttp.responseXML,"wfs","FeatureCollection")[0];
fm_nodes = getElements(fc_node,"gml","featureMember"); // many nodes
var results = "";
for (i=0; i<fm_nodes.length; i++)
{
	fm = fm_nodes[i]; // our individual FeatureMember
	gnis_node = getElements(fm,"topp","gnis")[0]; // get the next node: <topp:gnis>

	// now grab the values out of the XML tag and save them
	var type = getElements(gnis_node,"topp","type")[0].firstChild.nodeValue;
	var name = getElements(gnis_node,"topp","full_name")[0].firstChild.nodeValue;
	var province = getElements(gnis_node,"topp","sub_national")[0].firstChild.nodeValue;
	var country = getElements(gnis_node,"topp","country_name")[0].firstChild.nodeValue;

	geom = getElements(gnis_node, "topp", "the_geom")[0];
	point = getElements(geom, "gml", "Point")[0];
	feature_point = getElements(point,"gml","coordinates")[0].firstChild.nodeValue;

	var result = '<a href="javascript:zoomto(\''+feature_point+'\')">' + name + "</a> is a " + type + " in " + province + ", " + country;
	// "Vancouver is a Populated Place in BC, Canada"

	results += result + "<br>";
}

Now we will have the name (in this case 'Vancouver') showing up as a link, that when cliecked on, will call the function zoomTo() and pass it the feature's point location.
If you look at the source, it will look like this:
<a href="javascript:zoomto('-123.333333,49.25')">Vancouver</a> is a City in British Columbia, Canada<br>

On to the zoomto() function!

ZoomTo()

Here we get to play with MapBuilder again. This function is really simple:

function zoomto(point)
{
	s = point.split(",");
	x = parseFloat(s[0]);	// make into floats
	y = parseFloat(s[1]);

	// make new bounding box
	var ul = new Array();// x, y
	var br = new Array();// x, y
	ul[0] = x-0.15;
	ul[1] = y+0.15;
	br[0] = x+0.15;
	br[1] = y-0.15;

	config.objects.mainMap.extent.zoomToBox(ul, br);
}

The function takes in a point as a string. So the first thing we do is split it into two strings (x and y) by using the comma, then we make the strings into floats.
Next, we need to make a new bounding box. This bounding box will be the new view in the map.
We need to make two arrays, each with two values. Upper Left (ul) and Bottom Right (br). This defines our bounding box.

I took the point passed in and adjusted the values by +-0.15.
0.15 is a little big, so I suggest maybe 0.10.

The bounding box is ready so we need to tell MapBuilder to zoom to the new bounding box. This is done in one line:

config.objects.mainMap.extent.zoomToBox(ul, br);

The 'config' variable is part of map builder and represents the configuration of the system (map_config.xml). From the config, we grab all the objects, then ask for one in particular, 'mainMap'.
The mainMap value is defined in map_config.xml and is a "context" in the model. The context is what you are looking at. The "model" is what the entire map structure is made out of. This follows the Model-View-Controller design:
You have a "model" (map_config.xml), that is accessed with the "controller" (javascript functions and xslt) and everything is shown in the "view" (web browser).

Here is the line in the map_config.xml file that defines our main map:

<MapbuilderConfig>
	<models>
		<Context id="mainMap">

So far we have "config.objects.mainMap", now from that we need to get the extent of the map:

config.objects.mainMap.extent

The extent has a function called zoomToBox(a,b), cool, this is what we want.

So all we do is pass in our two arrays (the new bounding box) and the map automatically gets refreshed to the new view!!

config.objects.mainMap.extent.zoomToBox(ul, br);

Browser issues

Internet Explorer doesn't handle getElementById well. Especially if you start using the "hidden" style in your tags, or using <span>. For this you have to surround your tag in a <div>, give the <div> an id (<div id="cheap_hack">) and call getElementById("cheap_hack").getElementsByTagName('span');
In this case I had a <span> that was not being found in IE. So I had to get the span by using the tag name, ID and name didn't work.

SUMMARY

We first had to make a box that can scroll and contain our links of the search results. We did this using <div> tags and changing the style of it.

We then updated the search result parser to generate nice results in the form of links that would call our zoomTo javascript function.

Finally we made the zoomTo function that makes a bounding box out of the location's point, and tells MapBuilder to zoom to that location.

>>Proceed onto the next step, Step 6: Geocoder >>