views:

197

answers:

2

Simply, I seem to be able to write code that either creates a clickable marker for a pop-up infoWindow OR gets the bounds of the returned markers and resets the map extent and zoom levels. I can't seem to combine the two. My example below will nicely set the extent of the map to the results of the query. But I don't quite know how to include the addListener event to the marker given how my loop is structured, and that in the var mapOptions, I set center: gbounds.getCenter(). These two issues seem to complicate my ability to add events to the map or marker.

    <cfquery name="myquery" datasource="xxxx">
       SELECT name, lat, long
       FROM tblMain
    </cfquery>

    <cfset bridgeCoord=arrayNew(1)>

    <cfloop query="myquery">
   <cfset bridge[CurrentRow] = structNew()>
   <cfset bridge[CurrentRow].lat=lat>
       <cfset bridge[CurrentRow].long=longX>
          <cfset bridge[CurrentRow].name=name>
     </cfloop>



     <script>
       $(document).ready(function() {
       var gbounds = new google.maps.LatLngBounds();
        var markers = [];


       <cfloop index="mi" array="#bridge#">
           <cfoutput> 
               //make the point
               var point = new google.maps.LatLng(#mi.lat#,#mi.long#);
               gbounds.extend(point);
               //make the marker
               var marker = new google.maps.Marker({position: point, title:"#mi.name#"});
               markers[markers.length] = marker; 
            </cfoutput>
       </cfloop>

              var mapOptions = {
                    zoom:3,
                    mapTypeControl: true,
                    mapTypeControlOptions: {
                    style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
                       },
                     mapTypeId:google.maps.MapTypeId.ROADMAP,
                     center:gbounds.getCenter()
                        };



        var map = new google.maps.Map(document.getElementById('myMap'), mapOptions);

           map.fitBounds(gbounds);

           for(var i=0; i<markers.length; i++) markers[i].setMap(map);

      });

fyi, I've also tried organizing part of the code, like below. this works great for adding the click event to the marker but then I can't seem to use my center:gbounds.getCenter() or map.fitBounds() elements b/c the mapOptions get set first and passed to the new map variable and gbounds hasn't been defined by that point. Hardcoding lat/long to center: seems to just keep it there.

       function initialize() {

    var gbounds = new google.maps.LatLngBounds();    


  var myOptions = {
    zoom: 4,
    center: new google.maps.LatLng(39.0,-94.1),
    mapTypeControl: true,
    mapTypeControlOptions: {style: google.maps.MapTypeControlStyle.DROPDOWN_MENU},
    navigationControl: true,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  }
  map = new google.maps.Map(document.getElementById("myMap"),
                                myOptions);

  google.maps.event.addListener(map, 'click', function() {
        infowindow.close();
        });

  // Add markers to the map
  // Set up three markers with info windows 

    <cfloop index="mi" array="#bridge#">
        <cfoutput>

             //make the point
               var point = new google.maps.LatLng(#mi.lat#,#mi.long#);
            gbounds.extend(point);
            //make the marker
               var marker = createMarker(point, "#mi.name#", "#mi.name#")

        </cfoutput>
    </cfloop>


}
 map.fitBounds(gbounds);;

function createMarker(latlng, tip, html) {
    var contentString = html;
    var marker = new google.maps.Marker({
        position: latlng,
        title: tip,
        map: map,
        });

    google.maps.event.addListener(marker, 'click', function() {
        infowindow.setContent(contentString); 
        infowindow.open(map,marker);
        });
}


var infowindow = new google.maps.InfoWindow(
  { 
    size: new google.maps.Size(150,50)
  });

UPDATE 10/12: Got it to work. There is indeed a typo in Tyler's code but it's minor. I'm not sure how well I can take this code and expand on it since I'm not very versed in jQuery but this is a start. One thing it doesn't do that I'm struggling with is how to deal w/the map when zero markers are returned. It is possible in my query that no records can be returned. What happens is that the map draws but it is centered and zoomed in the middle of the ocean. I figured it should default to the center I set up (center: new google.maps.LatLng(39, -94.1) but it doesn't.

Replace: 

var $markers = $container.find(".map-marker");

with

var $markers = $container.find(".mapMarker");
A: 

EDIT

I actually just dealt with this a month or so ago, here is some modified code from what worked for me to make it more like yours.

Google Maps API v3 and jQuery 1.4.2 (though this should prolly work with any version of jQuery, and porting it to strait javascript or another library should be cake)

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script>
<script src="http://maps.google.com/maps/api/js?sensor=false" type="text/javascript"></script>

<cfquery name="mi" datasource="xxxx">
SELECT name, lat, long, content
FROM tblName
</cfquery>

<ul id="mapMarkers">
<cfoutput query="mi">
    <li class="mapMarker" data-latitude="#mi.lat#" data-longitude="#mi.long#">
        <div class="info-window">
        <h1 class="name">#mi.name#</a></h1>
        <div class="content">#mi.content#</div>
        </div>
    </li>
</cfoutput>
</ul>
<div id="map"></div>

<script type="text/javascript">
$(function() {
    var $container = $("#mapMarkers");

    var $map = $("#map");
    var $markers = $container.find(".map-marker");

    var bounds = new google.maps.LatLngBounds();
    var infowindow = new google.maps.InfoWindow({
        maxWidth: 300
    });

    var gmap = new google.maps.Map($map[0], {
        zoom: 8
        , mapTypeId: google.maps.MapTypeId.ROADMAP
    });

    $markers.each(function(){
        $this = $(this);
        var latitude = $this.attr("data-latitude");
        var longitude = $this.attr("data-longitude");
        var content = $this.find(".info-window").remove().html();
        var latlng = new google.maps.LatLng(latitude, longitude);

        bounds.extend(latlng);

        var marker = new google.maps.Marker({
            position: latlng
            , map: gmap
        });

        google.maps.event.addListener(marker, 'click', function() {
            infowindow.setContent(content);
            infowindow.open(this.map, this);
        });

        google.maps.event.addListener(gmap, 'click', function() {
            infowindow.close();
        });

        $this.click(function(e, el) {
            e.preventDefault();

            infowindow.setContent(content);
            infowindow.open(gmap, marker);
        })
    });

    if($markers.length > 1)
        gmap.fitBounds(bounds);
    else
        gmap.setCenter(bounds.getCenter());

    $container.hide();
});
</script>

END EDIT

First you have to move the map and mapOptions to the top. Then inside your loop just add the addListener to the map for the marker:

<cfloop index="mi" array="#bridge#">
   <cfoutput> 
       //make the point
       var point = new google.maps.LatLng(#mi.lat#,#mi.long#);
       gbounds.extend(point);
       //make the marker
       var marker = new google.maps.Marker({position: point, title:"#JSStringFormat(mi.name)#", map:map});
       google.maps.event.addListener(marker, 'click', function() {
           infowindow.setContent("#JSStringFormat(mi.content)#");
           infowindow.open(this.map, this);
       });
       markers[markers.length] = marker; 
    </cfoutput>
</cfloop>

Things to note:

I added JSStringFormat to the name and content variables.

There is a map option in the marker creation that you can just add "map:map" to it so you don't have to run the for loop at the bottom.

Also, since you are now creating the map ahead of the markers you will move the zoom and center options to the bottom.

if(markers.length > 1)  
    map.fitBounds(gbounds);  
else  
    map.setCenter(gbounds.getCenter());  

Also once last thing:

Instead of

markers[markers.length] = marker;

You can use this

markers.push(marker); 
Tyler Clendenin
Also for more accessibility, you should output the data into a table or ul and add attributes such as data-latitude="XX.XXXXXXX" and data-longitude="XX.XXXXXX". Then use semantic markup for the title and content. Then using jQuery or your jslibrary of choice run on ready a script to farm the data and generate the map in place of the aforementioned table or list.
Tyler Clendenin
Also, ditch the conversion to an array and just loop using the query itself.
Tyler Clendenin
I added the code to the top of my post, and by semantic, I mean that if the user does not have javascript enabled, the records and information will still be shown. This also helps with search engine indexing. Oh and there might be a bug or two in there. Nothing crazy though, maybe just misspelled something.
Tyler Clendenin
Well, this is great. I'm not going to get a chance to try it out until Monday but I'll let you know how it goes. The downside is that I'm self-taught in all this stuff so I don't really know jquery syntax. Would be nice if I could figure out how to do this straight in js so I can mash it up w/other demo/examples out there.
Matt
Well pretty much anything with a $ sign is jQuery. It's all very simple, and a great library to make javascript so much easier.
Tyler Clendenin
I recognize it as jQuery but as I'm getting up to speed on this coding and am not well-versed in it, it actually complicates my ability to understand and develop (for instance adding additional features from the other gmap js samples online.) I did manage to get this to work, although there was a typo (see above.) I'm still hoping I can figure out enough of the jquery and the way you set things up to be able to expand on it and add more functionality.
Matt
A: 

I'm not sure what the problem is but you may find it easier to debug (and extend) if you untangle your JavaScript and ColdFusion code. You could write all the map logic in JS and then use CF to create the data (array of lat/lng/title) that the JS will process.

<script>
// Data For Map Logic
var points = [];
<cfloop query="...">
  <cfoutput>
    points.push({lat: #db_lat#, lng: #db_lng#, etc...});
  </cfoutput>
</cfloop>

// Logic for setting map extent and creating pop-ups
for (var i = 0; i < points.length; i++) {
  // do your magic
}
</script>

Using this approach you could completely eliminate CF and the database during testing by hard-coding the points array.

Lawrence Barsanti
But ultimately I am populating the array from CF. I have seen numerous example of hard-coded arrays. My issue is using a dynamic query. I can try the order you have but I'm not sure I follow...
Matt