Glad to help, Joe.

I didn't mean to imply that your coding was sloppy! I'm just an inveterate
tweaker. :-) I understand fully about taking shortcuts to get a prototype
running. (Just don't reload your page more than 100 times in a day!)

You know, now that I think of it, it would be pretty easy to modify your
code to create a JavaScript object with all the geocoded data in it - and
then you can paste that object back into the source code and remove the
gecoding logic. This would make your demo load quite a bit faster.

Something like this...

Add this tag to your HTML:

<pre id="geocoded"></pre>

And run this (untested) JS code:

jQuery(function( $j ) {
    
    var offices = ['null'], nOffices = 150, nDone = 0;
    var $map2 = $j('#map2');
    
    $map2.jmap({
            mapCenter:[30.2687,-97.7452],
            mapZoom: 13
    });
    
    for( i = 1;  i <= nOffices;  i++ )
        geocodeOffice( i );
    
    function geocodeOffice( i ) {
        $map2.jmap( "searchAddress",
            { address :$j('#address'+i).val() },
            function( options, point ) {
                offices[i] =
                    '    { address:"' + address +
                    '", lat:' + point.y +
                    ', lng:' + point.x + '}';
                if( ++nDone == nOffices )
                    $j('#geocoded').html(
                        'var geocodedOffices = [\n' +
                            offices.join(',\n') +
                        '];\n'
                    );
            }
        );
    }
    
}); // End of DOM Ready

After all the offices are geocoded, the PRE should be filled in with JS
source code for the 'geocodedOffices' array.

Copy and paste that result into your JS source code, and now your production
code can be:

jQuery(function( $j ) {
    
    var nOffices = 150;
    var $map2 = $j('#map2');
    
    $map2.jmap({
            mapCenter:[30.2687,-97.7452],
            mapZoom: 13
    });
    
    for( i = 1;  i <= nOffices;  i++ )
        createMarker( i );
    
    function createMarker( i ) {
        var office = geocodedOffices[i];
        $map2.jmap("addMarker", {
            pointLatLng: [ office.lat, office.lng ],
            pointHTML: "<div style='width:200px;'><p>This office is located
at:</p><p><b>" + office.address + "</p></div>",
            centerMap: false
         });
    }

}); // End of DOM Ready

Maybe not necessary for your demo, but this is a pretty good trick for
anyone who is using the client geocoder and wants to cache a particular set
of locations without having to do any server coding.

-Mike

> From: Joe
> 
> Mike,
> 
> You are the man!  No seriously!  I fully understood WHY the 
> comment bubble HTML was only using the last iteration's 
> value, but I am no expert with closures.  The book jQuery in 
> Action touches on it, but I still haven't wrapped my head 
> around its power.  Thank you thank you thank you so much.  
> Your explanation is clear, concise and elegant.  I have 
> tested it out and it works great
> 
> 
> As far as the coding practices are concerned, I am aware of 
> all of the issues and whipped this together toward the end of 
> the day yesterday and was determined to get it to work today. 
>  Also, I am using a portal solution that uses Velocity 
> templates so the $ alias can't be used.
> And having to deploy the "theme" just to test in the 
> development environment coerced me to use sloppy practices.
> 
> In regards to the amount of queries to Google, I FULLY agree 
> with storing these values on the server instead of the client 
> doing the heavy lifting.  I need to SHOW this in order for it 
> to get accomplished, if you know what I mean.
> 
> Thanks again!
> 
> Joe
> 
> On Apr 26, 11:53 am, "Michael Geary" <[EMAIL PROTECTED]> wrote:
> > Joe, the problem is that your code has a single variable 
> "i". At any 
> > point in time, when you reference that variable you will get the 
> > *current* value of "i".
> >
> > While the for loop is running, "i" gets each of the values 
> you expect. 
> > But when is your callback function called? Much later, *after* the 
> > loop has finished running. At this point "i" has the last 
> value it was 
> > given in the loop, 150.
> >
> > (Note that your loop does not run from 1 through 150, but 1 through 
> > 149. The last time through the loop, the value is 149, and 
> then when 
> > the value reaches 150 the loop terminates - so "i" is 150 
> after that.)
> >
> > Then why is the marker created at the correct location? Because the 
> > searchAddress geocoding function calls the callback with 
> "point" set 
> > correctly. But "i" is your own variable, and there's only 
> one copy of 
> > it for the entire program.
> >
> > The easiest way to fix this is usually to use a closure. You could 
> > code it like this:
> >
> > jQuery(function( $ ) {
> >
> >     var $map2 = $('#map2');
> >
> >     $map2.jmap({
> >             mapCenter:[30.2687,-97.7452],
> >             mapZoom: 13
> >     });
> >
> >     for( i = 1; i < 150; i++ )
> >         createMarker( i );
> >
> >     function createMarker( i ) {
> >         $map2.jmap("searchAddress", { address 
> :$('#address'+i).val() 
> > },
> > function(options,point) {
> >             $map2.jmap("addMarker", {
> >                 pointLatLng: [point.y, point.x],
> >                 pointHTML: "<div 
> style='width:200px;'><p>This office 
> > is located at:</p><p><b>" + $('#address'+i).val() + "</p></div>" ,
> >                 centerMap: false
> >              });
> >          });
> >     }
> >
> > }); // End of DOM Ready
> >
> > The only real change to the code here is the addition of the 
> > createMarker() function. This function is called each time through 
> > your loop, and each time it's called it gets its *own* 
> local variable, 
> > also named "i". (It isn't significant that this name "i" is 
> the same 
> > name as the "i" outside the function - they are two different 
> > variables and could have different names.)
> >
> > So, when the searchAddress callback is called, it's using 
> the local "i"
> > inside that particular instance of the createMarker function, which 
> > will be the value you expect.
> >
> > I took the liberty of making a couple of unrelated changes to the 
> > code, just to illustrate some recommended coding techniques - in 
> > particular, the use of the $map2 variable instead of calling the 
> > jQuery function $('#map2') every time through the loop. Any 
> time you 
> > can move something outside a loop instead of calling it 
> repeatedly, it will help performance.
> >
> > One thing I didn't fix is the hard coded value of 150 (or 149). You 
> > probably don't want to just hard code this - what happens when you 
> > open another office? :-)
> >
> > HOWEVER...
> >
> > All that said, there is a much worse problem with the code, 
> one that 
> > we can't fix here.
> >
> > The Google client geocoder is limited to 15,000 queries per 
> day per API key.
> > At 150 per visit, you are going to hit that limit as soon 
> as your page 
> > is visited (or reloaded) 100 times. Then you will be blocked from 
> > further geocoding requests, typically for 24 hours.
> >
> > Even before you hit the limit, geocoding 150 locations is going to 
> > take a while.
> >
> > There's no way to fix this in JavaScript. Instead, you need 
> to geocode 
> > all of your locations ahead of time, save them in a 
> database or a file 
> > on your server, and code those latitude/longitude points 
> into your HTML/JavaScript.
> > This will eliminate all the need for geocoding on the 
> client. Your map 
> > will load *much* faster, and you won't have to worry about the 
> > geocoder API limit.
> >
> > -Mike
> >
> > > From: Joe
> >
> > > Check the following code:
> >
> > > $j = jQuery;
> >
> > > $j().ready(function()
> > > {
> >
> > > $j('#map2').jmap({
> > >                 mapCenter:[30.2687,-97.7452],
> > >                 mapZoom: 13
> > >         });
> >
> > >         for(i=1; i < 150; i++)
> > >         {
> >
> > >              $j('#map2').jmap("searchAddress", {address:
> > > $j('#address'+i).val() }, function(options,point)
> > >                   {
> > >                        $j('#map2').jmap("addMarker", {
> > >                                         pointLatLng:[point.y, 
> > > point.x],
> > >                                         pointHTML: "<div 
> style='width:
> > > 200px;'><p>This office is located at:</p><p><b>" +
> > > $j('#address'+i).val() + "</p></div>" ,
> > >                                         centerMap: false
> > >                                 });
> > >                 });
> >
> > >         }
> >
> > > }); // End of DOM Ready
> >
> > > In the markup there are 150 like these:
> >
> > > // "N" represents a number from 1 to 150
> >
> > > <input type="hidden" id="addressN" value="123 Main Street 
> New York, 
> > > NY 10101" />
> >
> > > Now when I throw in some alerts I notice that the 2nd 
> function (the 
> > > callback function) actually places the proper point, but does NOT 
> > > produce the proper HTML; for each point that is place the comment 
> > > bubble has the address of the LAST point that was placed 
> on the map.  
> > > Why would it place the point in the proper iteration, but not the 
> > > associated HTML?
> 

Reply via email to