1

I am developing a Web Based Application using PHP, MongoDB, Javascript, and Google Maps API V3.

I was able to easily generate and display markers on a google map by converting the MongoDB array using json_encode. Here's an example map with markers and infowindow.

Markers and InfoWindow

However, when I try to implement the Google Maps MarkerClusterer method, the markers disappears. I followed Google Map's "A Simple MarkerClusterer Example" as a guide.

I also tried declaring a global cluster object, passing it an empty array,

var markerCluster = new MarkerClustrer(map, markers);

then using markerCluster.addMarkers(markers, true); as alternate method with no luck.

It seems pretty simple but somehow, it is not displaying the markers. I also tried commenting out the whole infoWindow/OnClick event section so I don't think its related to that. Any help is greatly appreciated.

PHP MongoDB Query:

<?php

// Connect to Mongo and set DB and Collection
try
{
    $mongo = new Mongo();
    $db = $mongo->selectDB('twitter');
    $collection = $db->selectCollection('tweets');
}
catch(MongoConnectionException $e)
{
    die("Failed to connect to Twitter Database ". $e->getMessage());
}

// The hotspots array will contain the data that will be returned
$tweets = array();

// Return a cursor of tweets from MongoDB
$cursor = $collection->find();

// Try catch for catching whether there are tweets to display
$count = 0;
try 
{
    $count = $cursor->count();
} 
catch (MongoCursorException $e) 
{
    die(json_encode(array('error'=>'error message:' .$e->getMessage())));
}

// Loops through the cursor again specifically for querying all geo locations
// Unlike table display of tweets, this cursor is not limited by pages. 
foreach($cursor as $id => $value)
{ 
    $mapLocations[] = array
    (
        'id'=>$value['_id'],
        'screen_name'=>$value['screen_name'],
        'name'=>$value['name'],
        'tweet'=>$value['tweet'],
        'hashtags'=>$value['hashtags'],
        'lat'=>$value['geo']['lat'],
        'long'=>$value['geo']['long'],
        'date'=>$value['date'],
        'img'=>$value['img'],
        'specImg'=>$value['specImg']
    );
}
// var_dump($mapLocations);
?>

Javascript Function:

function initialize() 
{
    // Converts MongoDB information to JSON, ready for Javascript
    var tweets = <?php echo json_encode($mapLocations); ?>;

    // Sets google maps options
    var myOptions = 
    {
        // Centers on Maui...
        center: new google.maps.LatLng(20.80362, -156.321716),
        zoom: 7,
        mapTypeId: google.maps.MapTypeId.TERRAIN
    };

    // Sets Marker Clusterer Options
    var mcOptions =
    {
        gridSize: 50, maxZoom: 15
    };

    // Generates Google Map and applies the defined options above.
    var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

    // Infowindow for displaying information for onClick event  
    // Content must be inside the google.maps.event function 
    // Otherwise the same content will be entered on all markers    
    var infoWindow = new google.maps.InfoWindow({});

    var markerCluster = null;   // Initializes markerCluster
    var markers = [];           //Array needed to pass to MarkerClusterer

    // Loops through each tweet and draws the marker on the map.    
    for (var i = 0; i < tweets.length; i++)
    {
        var tweet = tweets[i];

        if(tweet.lat != null || tweet.long != null) 
        {
            var myLatLng = new google.maps.LatLng(tweet.lat, tweet.long);
            //document.write(" Latitude: " + tweet.lat + " Longitude: " + tweet.long + " <br> ");
            var marker = new google.maps.Marker({ 
                position: myLatLng,
                //icon: "markers/flag.png",
                //map: map,
            });

            markers.push(marker);

            google.maps.event.addListener(marker, 'click', (function(marker, i) 
            {
                return function() 
                {
                    // Generates a table for infoWindow
                    var content = "<table class='popup'>";

                    // Check if image exits, otherwise show no image icon
                    if(tweets[i].specImg != null) 
                    {
                        content += "<tr><th width=75 ><a href=" + tweets[i].specImg + ">";
                        content += "<img height=75 width=75 src=" + tweets[i].specImg + "></a>";
                    }
                    else
                    {
                        content += "<tr><th width=75><img height=75 width=75 src=images/noimage.jpg>";
                    }
                    // Concatanate screen name and tweet
                    // Will work on trimming information
                    content += "</th><td>" + tweets[i].screen_name + " says...<br>"; 
                    content += "''" + tweets[i].tweet +  "''<br>";
                    content += "on " + tweets[i].date + "</td>";
                    content += "</table>";

                    // Zoom into marker on click
                    map.setZoom(15);
                    map.setCenter(marker.getPosition());

                    // Sets the infoWindow content to the marker
                    infoWindow.setContent(content);
                    infoWindow.open(map, marker);
                }
            })(marker, i)); 
        }
    } 
    var markerCluster = new MarkerClusterer(map, markers);
}

@Robbie:

The JSONned $mapLocations becomes a multidimensional array but I simplified the $mapLocations to only store a 2D lat and long. The javascript source code becomes as follow.

var tweets = [{"lat":20.87179594,"long":-156.47718775},{"lat":20.87195633,"long":-156.47714356},{"lat":20.87138419,"long":-156.47719744},{"lat":21.3320704,"long":-157.8685716},{"lat":null,"long":null},{"lat":21.36509415,"long":-157.92824454},{"lat":21.3320825,"long":-157.8684742},{"lat":null,"long":null},{"lat":21.33673131,"long":-157.86824},{"lat":21.332507,"long":-157.86635342},{"lat":null,"long":null},{"lat":null,"long":null},{"lat":null,"long":null},{"lat":37.36520709,"long":-121.92386941},{"lat":37.2499758,"long":-121.86462506},{"lat":37.36278955,"long":-121.90521146},{"lat":null,"long":null},{"lat":37.36278955,"long":-121.90521146},{"lat":null,"long":null},{"lat":null,"long":null},{"lat":null,"long":null},{"lat":null,"long":null},{"lat":null,"long":null},{"lat":null,"long":null},{"lat":20.88944108,"long":-156.4761887},{"lat":37.36273157,"long":-121.90479984},{"lat":20.85102618,"long":-156.65936351},{"lat":20.88949978,"long":-156.4762491},{"lat":null,"long":null},{"lat":21.3320168,"long":-157.8685715},{"lat":null,"long":null},{"lat":null,"long":null},{"lat":null,"long":null}];
bryl
  • 21
  • 1
  • 5
  • I suspect the error is that you are doing `var tweets = ;` and then treating tweets as a JavaScript array. JSON is serialised JavaScript for data exchange; you need to `eval()` the JSON if you want to convert it to JavaScript objects. – Stennie Jun 25 '12 at 23:15
  • I used this website [Convert PHP array to Javascript](http://stackoverflow.com/questions/5618925/convert-php-array-to-javascript-array) to convert the PHP array to a javascript array. Seems to properly get the data. I also tried using eval(tweet.lat), eval(tweet.long) and I still get the markers working fine. However, when I try to implement the Marker Clusterer, the markers disappears. – bryl Jun 26 '12 at 01:13

2 Answers2

1

FINALLY FIGURED IT OUT:

As I expected, it was something very simple. Apparently you need to download the markerclusterer.js file from the Google Maps Utility Library. I thought the clusterer was already built in into the API itself.

I fixed it by downloading the script into server and referencing it like so

<script type="text/javascript" src="markerclusterer.js"></script>

Anyways, thanks everyone for the help!

bryl
  • 21
  • 1
  • 5
0

I think there may be a couple of issues:

  1. The infowindow is required per marker, you are sharing one infowindow for all the markers and repeatedly changing its contents.

  2. Also check that you don't fall into the closure trap. I could retpye it all, but you basically need to create a function out of the closure. There is an explination of how to do this at http://www.robertbolton.com/blog/google-maps-v3-multiple-markers-and-infowindows-in-a-loop

Robbie
  • 17,605
  • 4
  • 35
  • 72
  • The infowindow works fine without the MarkerClusterer. However, when I try to implement the cluster, all the marker does not show up. The map shows up, so it could be due to the markers array I'm passing the function? – bryl Jun 26 '12 at 01:15
  • I can't actually test, but the info windows should still work for ONE of the markers, I think. Try it with two markers and see what happens? If you can paste a JSONned $mapLocations into your question, I can try the JS using that and check my thinking. – Robbie Jun 26 '12 at 02:42
  • It works fine with multiple markers and displays the infowindow for each marker. I updated my question above and attached a screenshot of a working example without clustering. I also pasted the JSONed javascript output. I really appreciate your help. – bryl Jun 26 '12 at 08:37