• Tidak ada hasil yang ditemukan

Server-Side Common Point Method

As you would expect, there are both pros and cons to using the boundary method. The advantages are as follows:

• This technique uses the standard existing Google Maps API methods to create the markers on the map.

• It doesn’t drastically change your code from the simple examples presented earlier in the book.

• The PHP requires little server-side processing and little overhead.

The following are the boundary method’s disadvantages:

• It works for only dispersed data or higher zoom levels.

• It may not work for lower zoom levels, as too many markers will be shown at once.

• The client’s web browser makes a new request for markers after each map movement, which could increase server traffic.

//earth radius = 6378.8 kilometers or 3963 miles switch(strtolower($type)) {

case 'km': $r = 6378.8; break; //kilometers case 'm': $r = 3963; break; //miles

case 'n': $r = 3443.9; break; //nautical }

return acos(cos($a1)*cos($b1)*cos($a2)*cos($b2) + cos($a1)*sin($b1)*cos($a2)*sin($b2) +

sin($a1)*sin($a2)) * $r;

}

?>

The problem arises when you need to calculate the distance to every point in your data- base. Looping through each point is fine for a relatively small database, but when you are dealing with hundreds of thousands of points, you should first reduce your data set using other meth- ods. For example, you could limit the search to a certain range from the central point and construct a latitude/longitude boundary, as you did with the server-side boundary method in Listing 7-2. This would limit the surface distance calculation to each point in the bound- ary rather than the entire database. You could also look up the city or state when you geocode the address and filter your SQL query to points only in that city or state. Either way, it’s best to provide some level of additional search criteria so you don’t waste resources by calculat- ing distances to points on the other side of the world.

If you choose to use this method, also be aware that user interface problems may arise if you don’t design your interface correctly. The problem may not be obvious at first, but what hap- pens when you slide the map away from the common central point? Using strictly this method means no additional markers are shown outside those closest to the common point. Your users could be dragging the map around looking for the other markers that they know are there, but aren’t shown due to the restrictions of the central point location, as shown in Figure 7-3.

Figure 7-3. A map missing the available data outside the viewable area C H A P T E R 7■ O P T I M I Z I N G A N D S C A L I N G F O R L A R G E D ATA S E T S

156

Some maps we’ve seen use “closest to the center” of the map to filter points. This imposes the same ambiguity, as the map actually contains much more information but it’s simply ignored.

When using the server-side common point method, be sure to indicate to the users that the information on the map is filtered relative to the known point. That way, they are aware they must perform an additional search to retrieve more information.

Listings 7-4 and 7-5 show a working example of the common point method (http://

googlemapsbook.com/chapter7/ServerClosest/). To provide a simpler example, we’ve made the map clickable. The latitude and longitude of the clicked point is sent back to the server as the known point. Then, using the FCC tower database, the map will plot the closest 20 towers to the click. You could easily modify the example to send an address in the request and use a server-side geocoding application to encode the address into latitude and longitude coordi- nates, or you could use the API’s GClientGeocoderobject to geocode an address.

Listing 7-4. Client-Side JavaScript for the Closest to Common Point Method var map;

var centerLatitude = 42;

var centerLongitude = -72;

var startZoom = 10;

function init() {

map = new GMap2(document.getElementById("map"));

map.addControl(new GSmallMapControl());

map.setCenter(new GLatLng(centerLatitude, centerLongitude), startZoom);

//pass in an initial point for the center

updateMarkers(new GLatLng(centerLatitude, centerLongitude));

GEvent.addListener(map,'click',function(overlay,point) { //pass in the point for the center

updateMarkers(point);

});

}

function updateMarkers(point) { //remove the existing points map.clearOverlays();

//create the boundary for the data to provide //initial filtering

var bounds = map.getBounds();

var southWest = bounds.getSouthWest();

var northEast = bounds.getNorthEast();

var getVars = 'ne=' + northEast.toUrlValue() + '&sw=' + southWest.toUrlValue()

+ '&known=' + point.toUrlValue();

//log the URL for testing

GLog.writeUrl('server.php?'+getVars);

//retrieve the points

var request = GXmlHttp.create();

request.open('GET', 'server.php?'+getVars, true);

request.onreadystatechange = function() { if (request.readyState == 4) {

var jscript = request.responseText;

var points;

GLog.write(jscript);

eval(jscript);

//create each point from the list for (i in points) {

var point = new GLatLng(points[i].lat,points[i].lng);

var marker = createMarker(point);

map.addOverlay(marker);

} } }

request.send(null);

}

function createMarker(point) { var marker = new GMarker(point);

return marker;

}

window.onload = init;

Listing 7-5. Server-Side PHP for the Closest to Common Point Method

<?php

//surface distance calculation from Listing 7-3

function surfaceDistance($lat1,$lng1,$lat2,$lng2,$type='km'){

$a1 = deg2rad($lat1); //lat 1 in radians

$a2 = deg2rad($lat2); //lat 2 in radians

$b1 = deg2rad($lng1); //lng 1 in radians

$b2 = deg2rad($lng2); //lng 2 in radians

//earth radius = 6378.8 kilometers or 3963 miles switch(strtolower($type)) {

case 'km': $r = 6378.8; break; //kilometers case 'm': $r = 3963; break; //miles

case 'n': $r = 3443.9; break; //nautical }

C H A P T E R 7■ O P T I M I Z I N G A N D S C A L I N G F O R L A R G E D ATA S E T S 158

return acos(cos($a1)*cos($b1)*cos($a2)*cos($b2) + cos($a1)*sin($b1)*cos($a2)*sin($b2) + sin($a1)*sin($a2)) * $r;

}

//retrieve the variables from the GET vars

list($knownLat,$knownLng) = explode(',',$_GET['known']);

list($nelat,$nelng) = explode(',',$_GET['ne']);

list($swlat,$swlng) = explode(',',$_GET['sw']);

//clean the data

$knownLat=(float)$knownLat;

$knownLng=(float)$knownLng;

$nelng=(float)$nelng;

$swlng=(float)$swlng;

$nelat=(float)$nelat;

$swlat=(float)$swlat;

//connect to the database

require($_SERVER['DOCUMENT_ROOT'] . '/db_credentials.php');

$conn = mysql_connect("localhost", $db_name, $db_pass);

mysql_select_db("googlemapsbook", $conn);

/*

* Retrieve the points within the boundary of the map.

* For the FCC data, all the points are within the US so we

* don't need to worry about the meridian problem.

*/

$result = mysql_query(

"SELECT

longitude as lng,latitude as lat FROM

fcc_towers WHERE

(longitude > $swlng AND longitude < $nelng) AND (latitude <= $nelat AND latitude >= $swlat) ORDER BY

lat");

$list = $distanceList = array();

$i=0;

$row = mysql_fetch_assoc($result);

while($row) {

$i++;

extract($row);

$list[$i] = "p{$i}:{lat:{$lat},lng:{$lng}}";

$distanceList[$i] = surfaceDistance($lat,$lng,$knownLat,$knownLng,'km');

$row = mysql_fetch_assoc($result);

}

//sort the arrays by distance

array_multisort($distanceList,$list);

//free the distance list unset($distanceList);

//slice the array to the desired number of points //20 in this case

$list = array_slice($list,0,20);

//echo back the JavaScript object header('content-type:text/plain;');

echo "var points = {\n\t".join(",\n\t",$list)."\n}";

?>

You may notice the GETvariables for the request in Listing 7-4 contain the bounds of the viewable area along with the clicked point:

var getVars = 'ne=' + northEast.toUrlValue() + '&sw=' + southWest.toUrlValue()

+ '&known=' + point.toUrlValue();

As mentioned earlier, sending the bounds allows you to filter the points to the viewable area first, reducing the number of distance calculations. In Listing 7-5, the script simply records all the distances into the distanceListarray, and then sorts and slices the array by distance to the known point before returning the request.

The closest to common point method offers the following advantages:

• It works at any zoom level.

• It works for any sized data provided you add additional filtering.

• This method is great for relative location-based searches.

Its disadvantages are as follows:

• Each request must be calculated and can’t be easily cached.

• Not all available data points appear on the map.

• It requires a relative location.

• It may require server resources for larger/dense data sets.

C H A P T E R 7■ O P T I M I Z I N G A N D S C A L I N G F O R L A R G E D ATA S E T S 160