Adding markers to Google Maps from Excel

What can you learn here ?
  • Use Google Maps API
  • Plot markers on a map
  • Add Popup data

Put addresses on a map as markers get it now

We've already looked at how to geoCode addresses using various APIs. Now lets look at using Google Maps to put markers on a Map using an Excel worksheet as input. As usual you can find the completed example in the googleMapping.xlsm download. Before running this please check the parameter worksheet and change the filename value  for the place that the application will generate a web page to something relevant for your machine. Look for the  filename in the Marker html section of the Parameter Worksheet.


Putting markers on a map for our 'Customer Sheet'

If you've been following along with the geoCoding examples you will already be familiar with our test data, which we've now already geocoded from input addresses of various levels of accuracy. You can also customize the markers and add heatmaps and circles - see Overlaying circles and heatmaps


The objective of this section is to plot this sheet on a map, with a marker for each row. In addition we are also going to have a popup box that will show the customer name and the full address. The final map using our test data looks like this.  


Parameters

Like many of the examples on this site, we are using parameters to drive the behavior of the code. Many of the parameters used in the geoCoding examples are used, but we have a couple of new sections in the parameter worksheet. 

4 pieces of data are needed for placing markers on the map are given under the 'Markers' parameter block. This instructs the application which columns in the table contain these values.

Preparing the Content

The title Marker Parameter specifies the column containing the value you want shown in reaction to a mouse hover over the map marker. In this case, we want to show the customer name. The content marker parameter  specifies the column containing the value you want to show in the pop-up box in reaction to a click on the map marker. In this case, we want to create a description of the customer and his address, and we can use html to construct it if necessary. You cab see that I've added a content field in our example application that looks like the below, but you can of course put whatever you want in there. A blank content field will mean that you don't need a pop up box for that marker.
="<b>" & B2 & "</b><br>" & SUBSTITUTE(L2,",","<br>") & "<br>"

which translates to
<b>Acme</b><br>1600 Amphitheatre Pkwy<br> Mountain View<br> CA 94043<br> USA<br>

Customizing the generated web page

The application creates , and displays, a web page in the place you specify in the filename parameter of the Marker Html section of the Parameter WorkSheet.
 

The other parameters in this section contain the Code  needed to generate this web page.  Header, main and functions are largely  javaScript and are responsible for interpreting the Excel data and plotting it on the map.  You can change these if you want of course, but only if you want to change the functionality of the application.

The body section however is likely going to need to be customized to design whatever web page you want to create. Right now it just looks like this

<body onload="initialize()">
 <div id="comboquicklink" class = "mcquicklink">Quick Links</div>
 <div id="combocategory" class = "mccategory"></div>
 <div id="heading" class ="mcheading">Excel and Google Mapping Integration</b>
 
 <a href="http://rambings.mcpher.com">
  ramblings.mcpher.com</a></div>
 <div id="map_canvas" style="width: 100%; height: 90%"></div>
    
</body>

As long as you retain the  <body onload="initialize()"> (load up the map when the page loads) and <div id="map_canvas"></div>  (the place on the web page to contain the map), you can tailor this body parameter as as much as you like. 

Compare Google Maps to others

I've implemented this exact same thing on other APIs as below.  Take a look to compare. My recommendation is to stick with Google Maps.

The generated html file

When you execute the code for marking up the customer worksheet,  an html file is generated in the location you have specified and IE is kicked off to display it. (You may get a security warning about running local content on IE depending on your settings). The generated file from our example application looks like this. It is just the head, main, body and functions code from the Parameter sheet, plus the function mcpherDataPopulate(), which is generated at run time and contains the Excel data that will be displayed on the map.  By default, the map will be displayed at zoom level 2, but you can change that with a Url parameter, for example 

Finally, the map will be centered around the first row in your data and will be a standard google Map. If you wanted to change that default (for example to be centered around your head office, with a satellite view), then you would need to change the code section below of the main parameter 
     
var myOptions = {
         center: new google.maps.LatLng(mcpherData.cJobject[0].lat,
                 mcpherData.cJobject[0].lng),
        mapTypeId: google.maps.MapTypeId.ROADMAP

Generated html file contents

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css">
  html { height: 100% }
  body { 
 height: 100%; 
 margin: 0; 
 padding: 0 ;  
 color: Gray ;
 background-color: WhiteSmoke; 
  }
  #map_canvas { height: 100%}

.mcquicklink {
 margin-left: 20px;
 margin-right: 20px;
 display: inline;
 font-family: sans-serif;
 float: right;
}
.mccategory {
 margin-left: 20px;
 margin-right: 20px;
 display: inline;
 font-family: sans-serif;
 float: right;
}
.mcheading {
 display: inline;
 font-family: sans-serif;

 
}
</style>
<script type="text/javascript"
    src="http://maps.googleapis.com/maps/api/js?sensor=false">
</script>
<script type="text/javascript">

function mcpherDataPopulate() { 
var mcpherData = {   
    "framework":{ "control": { "zoomlevelonselect":10, "resizeboundingbox": true}}, 
    "cJobject":[      {
         "title":"Rosemount theatre",
         "content":"\<b\>Rosemount theatre\</b\>\<br\>5400 North River Road\<br\> Rosemont\<br\> IL 60018\<br\> USA\<br\>",
         "lat":"41.9788038",
         "lng":"-87.8624106",
         "color":"#0000FF",
         "size":"74848.2063701911",
         "category":[            {
               "city":"Rosemont"
            },
            {
               "country":"United States"
            }
          ],
         "image":""
      },
      {
         "title":"UNO Lakefront Arena",
         "content":"\<b\>UNO Lakefront Arena\</b\>\<br\>6801 Franklin Avenue\<br\> University of New Orleans-East Campus\<br\> New Orleans\<br\> LA 70122\<br\> USA\<br\>",
         "lat":"30.030512",
         "lng":"-90.04931",
         "color":"#00728D",
         "size":"112837.916709551",
         "category":[            {
               "city":"New Orleans"
            },
            {
               "country":"United States"
            }
          ],
         "image":""
      },
      {
         "title":"Intex",
         "content":"\<b\>Intex\</b\>\<br\>Nankokita\<br\> Suminoe Ward\<br\> Osaka\<br\> Osaka Prefecture 559-0034\<br\> Japan\<br\>",
         "lat":"34.6381228",
         "lng":"135.4253848",
         "color":"#00728D",
         "size":"112837.916709551",
         "category":[            {
               "city":"Suminoe Ward"
            },
            {
               "country":"Japan"
            }
          ],
         "image":""
      },
      {
         "title":"olympic park",
         "content":"\<b\>olympic park\</b\>\<br\>Olympic Park\<br\> Bangi-dong\<br\> Seoul\<br\> South Korea\<br\>",
         "lat":"37.5198209",
         "lng":"127.1227901",
         "color":"#FF5C00",
         "size":"298541.066072092",
         "category":[            {
               "city":"Seoul"
            },
            {
               "country":"South Korea"
            }
          ],
         "image":""
      },
      {
         "title":"aktiv square",
         "content":"\<b\>aktiv square\</b\>\<br\>Bang Yai\<br\> Nonthaburi\<br\> Thailand\<br\>",
         "lat":"13.8973058",
         "lng":"100.3910926",
         "color":"#00936C",
         "size":"123607.744647421",
         "category":[            {
               "city":""
            },
            {
               "country":"Thailand"
            }
          ],
         "image":"http://maps.google.com/mapfiles/marker_orange.png"
      },
      {
         "title":"red square",
         "content":"\<b\>red square\</b\>\<br\>Moscow\<br\> Russia\<br\>",
         "lat":"55.7512419",
         "lng":"37.6184217",
         "color":"#FF0000",
         "size":"328976.232123977",
         "category":[            {
               "city":"Moscow"
            },
            {
               "country":"Russia"
            }
          ],
         "image":"http://maps.google.com/mapfiles/marker_orange.png"
      },
      {
         "title":"heineken hall",
         "content":"\<b\>heineken hall\</b\>\<br\>ArenA Boulevard 590\<br\> Bullewijk\<br\> 1101 DS Amsterdam-Zuidoost\<br\> The Netherlands\<br\>",
         "lat":"52.3123574",
         "lng":"4.9441509",
         "color":"#001BE4",
         "size":"83682.8387188401",
         "category":[            {
               "city":"Amsterdam-Zuidoost"
            },
            {
               "country":"The Netherlands"
            }
          ],
         "image":""
      }
    ]};
return mcpherData; };

           function initialize() {
                mcpherData = mcpherDataPopulate();

                if (mcpherData.cJobject.length > 0) {
                    mcpherData.cJobject.sort(function(a, b) {
                        return a.title.toLowerCase() < b.title.toLowerCase() ? -1 : (a.title.toLowerCase() > b.title.toLowerCase() ? 1 : 0);
                    });

                    var myOptions = {
                        center: new google.maps.LatLng(mcpherData.cJobject[0].lat, mcpherData.cJobject[0].lng),
                        mapTypeId: google.maps.MapTypeId.ROADMAP
                    };

                    // get parameters if any
                    var qparams = mcpherGetqparams();

                    var cj = mcpherData.cJobject;
                    var bounds = new google.maps.LatLngBounds();
                    for (var i = 0; i < cj.length; i++) {
                        bounds.extend(new google.maps.LatLng(cj[i].lat, cj[i].lng));
                    }

                    if (!qparams['zoom']) qparams['zoom'] = 2;
                    myOptions['zoom'] = parseInt(qparams['zoom']);
                    // create the map
                    gMap = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
                    gMap.fitBounds(bounds);

                    // add the excel data
                    for (var i = 0; i < cj.length; i++) {
                        mcpherAddMarker(cj[i]);
                    }

                    // Set up combox
                    
                    category();
                    quickLink(-1);
                    makeFlightGroups();
                    initialCategory();
                }
            }

// flightpath stuff
                    function toType(obj) {
                        return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
                    }

                    function isArray(arg) {
                        return toType(arg) == 'array';
                    }

                    function createFlightGroups() {
                        var flightGroups = [];
                        var cj = mcpherData.cJobject;
                        var fg;
                        for (var i = 0; i < cj.length; i++) {
                            if (fg = cj[i].flightGroup) {
                                if (flightGroups.indexOf(fg) == -1) {
                                    flightGroups.push(fg);
                                }
                                cj[i].flightGroupIndex = flightGroups.indexOf(fg);
                            } else cj[i].flightGroupIndex = -1;
                        }
                        return flightGroups;
                    }

                    function initialCategory() {
                        // sets initial category using the first select element
                        var cj = mcpherData.cJobject;
                        var c, p;
                        var combos = getAllCombos();
                        if (combos.length > 0) {
                            var cats = createCategories(combos[0]);
                            for (var i = 0; i < cj.length; i++) {
                                if (c = cj[i].startCategory) {
                                    if ((p = cats.indexOf(c)) != -1) {
                                        var elem = document.getElementById('selectcategory0');
                                        elem.value = p;
                                        dealWithCategories(elem);
                                        return p;
                                    }
                                }
                            }
                        }
                        return -1;
                    }

                    // this is because IE doesnt have indexOf...
                    if (!Array.indexOf) {
                        Array.prototype.indexOf = function(obj, start) {
                            for (var i = (start || 0); i < this.length; i++) {
                                if (this[i] == obj) {
                                    return i;
                                }
                            }
                            return -1;
                        }
                    }
                    function resetBoundingBox(force) {
                        var z = mcpherData.framework && mcpherData.framework.control 
                                    && mcpherData.framework.control.resizeboundingbox ? 
                                        mcpherData.framework.control.resizeboundingbox : false;
                        if((z || force) && !vMap) {
                            // dont do for vizmap apps - reset bounding box to visible items
                            var cj = mcpherData.cJobject;
                            var bounds = new google.maps.LatLngBounds();
                            for (var i = 0; i < cj.length; i++) {
                                if (cj[i].marker.visible)bounds.extend(new google.maps.LatLng(cj[i].lat, cj[i].lng));
                             }
                            gMap.fitBounds(bounds);                            
                        } 
                    }

                    function makeFlightGroups() {
                        var flightGroups = createFlightGroups();
                        var cj = mcpherData.cJobject;

                        for (var j = 0; j < flightGroups.length; j++) {
                            var flightCoords = [];
                            var color = "#FF0000";
                            for (var i = 0; i < cj.length; i++) {
                                if (j == cj[i].flightGroupIndex) {
                                    flightCoords.push(cj[i].marker.position);
                                    if (cj[i].flightColor) color = cj[i].flightColor;
                                }
                            }
                            if (flightCoords.length) {
                                flightGroups[j].flightPath = new google.maps.Polyline({
                                    path: flightCoords,
                                    strokeColor: color,
                                    strokeOpacity: 1.0,
                                    strokeWeight: 2,
                                    map: gMap
                                });
                            }
                        }
                        return flightGroups;
                    }

                    function mcpherAddMarker(cj) {
                        var p = new google.maps.LatLng(cj.lat, cj.lng);
                        cj.circle = null;
                        if (cj.size) {
                            var circle = {
                                strokeColor: cj.color,
                                strokeOpacity: 0.8,
                                strokeWeight: 1,
                                fillColor: cj.color,
                                fillOpacity: 0.20,
                                map: gMap,
                                center: p,
                                radius: parseFloat(cj.size),
                            };
                            var drawCirle = new google.maps.Circle(circle);
                            cj.circle = drawCirle;

                        }

                        var marker = new google.maps.Marker({
                            position: p,
                            map: gMap,
                            title: cj.title
                        });
                        cj.marker = marker;
                        cj.infowindow = null;
                        if (cj.image) marker.setIcon(cj.image);
                        if (cj.content) {
                            cj.infowindow = new google.maps.InfoWindow({
                                content: cj.content
                            });
                            google.maps.event.addListener(cj.marker, 'click', function() {
                                cj.infowindow.open(gMap, cj.marker);
                                adjustZoom(cj);
                            });
                        }

                        return cj.marker;
                    }
                    function adjustZoom(cj){
                        var z = mcpherData.framework && mcpherData.framework.control 
                                    && mcpherData.framework.control.zoomlevelonselect ? 
                                        parseInt(mcpherData.framework.control.zoomlevelonselect) : null;
                        if (z) {
                            if (gMap.getZoom() == z) {
                                resetBoundingBox(true);
                            }
                            else {
                                gMap.setZoom(z);
                                gMap.setCenter(cj.marker.getPosition());
                            }
                        }
                        
                    }
                    function mcpherGetqparams() {
                        var qparams = new Array();
                        var htmlquery = window.location.search.substring(1);
                        var htmlparams = htmlquery.split('&');
                        for (var i = 0; i < htmlparams.length; i++) {
                            var k = htmlparams[i].indexOf('=');
                            if (k > 0) qparams[htmlparams[i].substring(0, k)] = decodeURI(htmlparams[i].substring(k + 1));
                            return qparams;
                        }
                    }


                    function dealWithQuickLink(selValue) {
                        if (vMap) {
                            var nextSpot = vMap.spots[selValue]; 
                            vMap.gotoAnotherSpot(vMap.currentSpot,nextSpot);
                            if (vMap.provider=='maps') nextSpot.createInfoWindow(0);
                        }
                        else {
                            var cj = mcpherData.cJobject[selValue];
                            if (cj.infowindow) {
                                cj.infowindow.open(gMap, cj.marker);
                            }
                            adjustZoom(cj);
                            return cj.infowindow;
                        }
                    }
                    function findMySpot(cj) {
                        // return the spot to which this cj belongs
                        if(vMap) {
                            for (var i=0; i < vMap.spots.length ; i++ ) {
                                if ( cj.SpotID === vMap.spots[i].spotId) return (vMap.spots[i]);
                            }
                            return null;
                        }
                        else
                            return cj;
                        
                    }

                    function dealWithCategories() {
                        var combos = getAllCombos();
                        // start by not showing anything, except where theres no categories
                        var cj = mcpherData.cJobject;
                        for (var j = 0; j < cj.length; j++) {
                            var mySpot = findMySpot(cj[j]);
                            mySpot.marker.setVisible(combos.length == 0);
                            if (mySpot.circle) {
                                    mySpot.circle.setVisible(combos.length == 0);
                                }
                        }
                        // need appear in each category

                        for (var j = 0; j < cj.length  && combos.length > 0; j++) {
                            var show = [];
                            for (var i = 0; i < combos.length ; i++) {
                                var selElem = document.getElementById('selectcategory' + i);
                                if (selElem.value != 0) {
                                    // we have a filter operating
                                    var cats = createCategories(combos[i]);
                                    var target = cats[selElem.value];
                                    if (vMap) {
                                        var c = cj[j];
                                        if (c.hasOwnProperty(combos[i])) {
                                            if(c[combos[i]] == target) show[i]=true ;
                                        }
                                    }
                                    else {
                                        for (var k = 0; k < cj[j].category.length ; k++) {
                                            for (m in cj[j].category[k]) {
                                                if (m == combos[i]) {
                                                    if(cj[j].category[k][m] == target)show[i]=true;
                                                }
                                            }
                                        }
                                    }
                                }
                                else {
                                    show[i]= true;
                                }
                            }
                            // show it?
                            var x=0;
                            for (var k=0; k < combos.length; k++ ) {
                                if (show[k] === true) x++;
                            }
                            if (x == combos.length) {
                                var mySpot = findMySpot(cj[j]);
                                mySpot.marker.setVisible(true);
                            
                                if (mySpot.circle) {
                                    mySpot.circle.setVisible(true);
                                }
                            }
                        }
                        // reset to show only filtered spots
                        quickLink();

                    }
                    
                function quickLink(selCategory) {
                        var comboElem = document.getElementById('comboquicklink');

                        if (comboElem) {
                            if (selCategory == -1) { //first time in
                                var selElem = document.createElement('select');
                                selElem.id = "quickLinks";
                            } else {
                                var selElem = document.getElementById('quickLinks');
                                selElem.options.length = 0;
                            }
                            // depends on type of app
                            var cj = vMap ? vMap.spots :  mcpherData.cJobject ;

                            mcpherAddEvent(selElem, "change", function() {
                                dealWithQuickLink(selElem.value);
                            }, false, true);

                            for (var i = 0; i < cj.length; i++) {
                                // only show visible spots
                                var workit = true;
                                if (cj[i].marker) workit = cj[i].marker.visible;
                                if (workit) { 
                                    var o = document.createElement('option');
                                    o.text = cj[i].title;
                                    o.value = i;
                                    selElem.value = o.value;
                                    try {
                                        selElem.add(o, null);
                                    } catch (error) {
                                        selElem.add(o);
                                    }
                                }
                            }

                            comboElem.appendChild(selElem);
                        }
                        resetBoundingBox();
                    }

                    function createCategories(categoryName) {
                        var cats = [];
                        var cj = mcpherData.cJobject;
                        for (var i = 0; i < cj.length; i++) {
                            // find the matching object
                            if (vMap) {
                                if(cj[i].hasOwnProperty(categoryName)) {
                                    if (cats.indexOf(cj[i][categoryName]) == -1) cats.push(cj[i][categoryName]);
                                }
                            } 
                            else {
                                for (var j = 0; j < cj[i].category.length; j++) {
                                    if (cj[i].category[j].hasOwnProperty(categoryName)) {
                                        if (cats.indexOf(cj[i].category[j][categoryName]) == -1) cats.push(cj[i].category[j][categoryName]);
                                    }
                                }
                            
                            }

                        }
                        cats.sort().splice(0, 0, 'all categories');
                        return cats;
                    }



                    function category() {

                        var comboElem = document.getElementById('combocategory');
                        if (comboElem) {
                            var combos = getAllCombos();
                            for (var j = 0; j < combos.length; j++) {
                                var selElem = document.createElement('select');
                                selElem.id = "selectcategory" + j;
                                var cats = createCategories(combos[j]);
                                mcpherAddEvent(selElem, "change", function() {
                                    dealWithCategories(selElem);
                                }, false, true);

                                for (var i = 0; i < cats.length; i++) {
                                    var o = document.createElement('option');
                                    o.text = cats[i];
                                    o.value = i;
                                    try {
                                        selElem.add(o, null);
                                    } catch (error) {
                                        selElem.add(o);
                                    }
                                }
                                var d = document.createElement('span');
                                var t = document.createTextNode(combos[j]);
                                d.appendChild(t);
                                d.appendChild(selElem);
                                comboElem.appendChild(d);

                            }
                        }
                    }

                    function getAllCombos() {
                        var combos = [];
                        if (vMap) {
                            for (var i = 0; vMap.framework.spots.categories && i < vMap.framework.spots.categories.length; i++)
                                combos.push(vMap.framework.spots.categories[i]);
                        }
                        else {
                            var cj = mcpherData.cJobject;
                            for (var i = 0; i < cj.length; i++) {
                                if (isArray(cj[i].category)) {
                                    for (var j = 0; j < cj[i].category.length; j++) {
                                        for (k in cj[i].category[j])
                                        if (combos.indexOf(k) == -1) {
                                            combos.push(k);
                                        }
                                    }
                                }
                            }
                        }
                        return combos;
                    }


            function mcpherAddEvent(o, e, f, b, complain) {
                // because IE is different
                if (o.addEventListener) return (o.addEventListener(e, f, b));

                else if (o.attachEvent) return (o.attachEvent('on' + e, f));
                else if (complain) alert('browser doesnt support events');

                return (null);

            }

            var mcpherData;
            var gMap;
            var vMap;
        </script>
    </head>

<body onload="initialize()">
 <div id="comboquicklink" class = "mcquicklink">Quick Links</div>
 <div id="combocategory" class = "mccategory"></div>
 <div id="heading" class ="mcheading">Excel and Google Mapping Integration</b>
 
 <a href="http://rambings.mcpher.com">
  ramblings.mcpher.com</a></div>
 <div id="map_canvas" style="width: 100%; height: 90%"></div>
    
</body>



The Vba code

Most of the code used here is already covered in Creating Google Maps Layers from Excel DatajsonData Manipulation Classes, and Google Visualization but here is the specific code for creating the html file from the Customer Worksheet.

' This will take the geocoded data and mark it
Option Explicit
'for more about this
' http://ramblings.mcpher.com/Home/excelquirks/classeslink/data-manipulation-classes
'to contact me
' http://groups.google.com/group/excel-ramblings
'reuse of code
' http://ramblings.mcpher.com/Home/excelquirks/codeuse
Public Sub genericMarking(paramName As String, markerHtml As String, _
                    Optional eOutput As eOutputMarkers = eOutputHtml)
    Dim dSets As cDataSets, dc As cCell, job As cJobject, fName As String
    Dim dr As cDataRow, vc As cCell, a As Variant, i As Long
    Set dSets = dSetsSetup(paramName)
    If dSets Is Nothing Then Exit Sub
    
    ' check that we have the required marker fields
    With dSets
        For Each dc In .dataSet(cMarkers).column("Column Name").rows
            If .dataSet(cMarkers).isCellTrue(dc.row, "required") Then
                With .dataSet(cMaster).headingRow
                    If Not .validate(True, dc.toString) Then Exit Sub
                End With
            End If
        Next dc
    End With
    ' we have it all now create a job

    Set job = New cJobject
    With job.init(Nothing)
 
    ' add framework/control parameters
        With .add("framework").add("control")
            For Each dr In dSets.dataSet(cControl).rows
                .add dr.value(cControl), dr.value(cControlValue)
            Next dr
        End With
    
        With .add("cJobject").addArray
            For Each dr In dSets.dataSet(cMaster).rows
                With .add
                    For Each dc In dSets.dataSet(cMarkers).column(cMarkers).rows

                        If Not dSets.dataSet(cMarkers).isCellTrue(dc.row, "array") Then
                            Set vc = dr.cell(dc.parent.cell("Column Name").toString)
                            If Not vc Is Nothing Then
                                .add dc.toString, vc.toString
                            End If
                        Else
                            a = Split(dc.parent.cell("Column Name").toString, ",")
                            With .add(dc.toString).addArray
                                For i = LBound(a) To UBound(a)
                                    Set vc = dr.cell(CStr(a(i)))
                                    If Not vc Is Nothing Then
                                        .add.add CStr(a(i)), vc.toString
                                    End If
                                Next i
                            End With
                        End If
                    Next dc
                End With
            Next dr
        End With
    End With
    
    ' now create the html file and browse to it

    fName = dSets.dataSet(markerHtml).cell("filename", "code").toString
    Select Case eOutput
        Case eOutputHtml
            If openNewHtml(fName, generateHtml(job, dSets, markerHtml)) Then
                pickABrowser dSets, fName, True
            End If
            
        Case eOutputKML
            If openNewHtml(fName, generateKML(job, dSets)) Then
                pickABrowser dSets, fName, True
            End If
        
        Case Else
            Debug.Assert False
            
    End Select
    dSets.tearDown
    Set dSets = Nothing
    
End Sub

Private Function generateHtml(job As cJobject, dSets As cDataSets, mHtml As String) As String
    Dim s1 As String

    ' the deserialized data
    s1 = "function mcpherDataPopulate() { " & vbCrLf & _
            "var mcpherData = " & job.serialize(True) & ";" & vbCrLf & _
            "return mcpherData; };"


    With dSets.dataSet(mHtml)
        generateHtml = _
        .cell("header", "code").toString & vbCrLf _
        & s1 & vbCrLf _
        & .cell("main", "code").toString & vbCrLf _
        & .cell("catfunctions", "code").toString & vbCrLf _
        & .cell("functions", "code").toString & vbCrLf _
        & .cell("body", "code").toString & vbCrLf
    End With
    
End Function
Private Function generateKML(job As cJobject, dSets As cDataSets) As String
    Dim s1 As String
    Dim jo As cJobject
    
    s1 = vbNullString
    For Each jo In job.children
        s1 = s1 & generatePlaceMark(jo)
    Next jo
    
    generateKML = _
         "<?xml version='1.0' encoding='UTF-8'?>" & vbLf & _
         "<kml xmlns='http://www.opengis.net/kml/2.2'>" & vbLf & _
         tag("Document", s1) & "</kml>"
    
    
End Function

Private Function generatePlaceMark(job As cJobject) As String
    ' convert jSon item to KML
    With job
        generatePlaceMark = _
            tag("Placemark", _
            tag("name", .child("title").toString) & _
            tag("description", "<![CDATA[" & .child("content").toString & "]]>") & _
            tag("Point", _
                tag("coordinates", .child("lng").toString & "," & .child("lat").toString) & ",0"))
    End With
End Function
Private Function tag(tagName As String, Optional item As String = vbNullString) As String
    tag = "<" & tagName & ">" & vbLf & item & vbLf & "</" & tagName & ">"

End Function



Comments