When I’m watching my 3D choropleth maps in Google Earth, strange "holes" appear in polygons representing countries with low values on a statistical indicator (e.g. having a low altitude value). The map below (KML) shows the digital divide in the world (number of Internet users):
The hole in India:

KML has three parameters for controlling the behaviour of polygons; extrude, tessellate and altitudeMode. By setting altitudeMode to clampToGround, my country polygons follow the great circle and get a solid fill. The problem arises when I extrude the polygons by adding an altitude representing a statistical value. Only the vertices of the polygon are extruded to the given altitude, and not the centre of geometry. I miss a clampToAltitude option in KML. A shape should follow earth’s profile, at the requested height above the surface.
There are some workaround to this problem:
The hole in India:
KML has three parameters for controlling the behaviour of polygons; extrude, tessellate and altitudeMode. By setting altitudeMode to clampToGround, my country polygons follow the great circle and get a solid fill. The problem arises when I extrude the polygons by adding an altitude representing a statistical value. Only the vertices of the polygon are extruded to the given altitude, and not the centre of geometry. I miss a clampToAltitude option in KML. A shape should follow earth’s profile, at the requested height above the surface.
There are some workaround to this problem:
- Give all polygons a minimum altitude to support a "flat roof". Has to be a high value for a country like Russia.
- Break up large polygons into smaller pieces.
- Add additional clampToGround polygons to "hide" the holes. Only works with solid fills (no transparency).
Comments
The examples I've provided already have tesselation enabled. According to the KML 2.2 Reference, tesselation only works when the value of altitudeMode is clampToGround. That is why polygons without an altitude value are displayed properly.
see code
earth.getWindow().setVisibility(true);
var data = new google.visualization.DataTable(jsonData);
var options = {
type: 'prism',// 'choropleth',//
title: 'Call patterns',
maxHeight: 600000,
colorType: 'scale',
classification: 'equal',
geometry: worldBorders
};
var kml = map.draw(data, options);
kml = ''+kml;
kml = kml.replace('\\\\\\','');
if (window.DOMParser)
{
parser=new DOMParser();
xmlDoc=parser.parseFromString(kml,"text/xml");
}
else // Internet Explorer
{
xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async="false";
xmlDoc.loadXML(kml);
}
var root = xmlDoc.documentElement;
var placemarks = root.getElementsByTagName('Placemark');
// find the maximum value of the entire map
var maxValue = -1;
for (var i=0; i < placemarks.length; i++) {
var aPlaceMarkName = placemarks[i].getElementsByTagName('name')[0];
var myRegexp = /.+\s(\d+,\d+|\d+)/;
var match = myRegexp.exec(aPlaceMarkName.childNodes[0].nodeValue);
var curValue = parseInt(match[1].replace(',',''));
if(curValue>maxValue)
maxValue = curValue;
}
//change within the kml file, the altitudeMode tag of those elements having value less than 10% of the maximum
for (var i=0; i < placemarks.length; i++) {
var aPlaceMarkName = placemarks[i].getElementsByTagName('name')[0];
var myRegexp = /.+\s(\d+,\d+|\d+)/;
var match = myRegexp.exec(aPlaceMarkName.childNodes[0].nodeValue);
var curValue = parseInt(match[1].replace(',',''));
if (curValue < (maxValue/10) /*|| curValue<1000*/)
{
var altitudeModeValues = placemarks[i].getElementsByTagName('altitudeMode');
for (var j=0; j < altitudeModeValues.length; j++) {
altitudeModeValues[j].childNodes[0].nodeValue = 'clampToGround';
}
placemarks[i].getElementsByTagName('tessellate')[0].childNodes[0].nodeValue = 0;
}
}
if (window.DOMParser)
kmlString = (new XMLSerializer()).serializeToString(xmlDoc);
else
kmlString = xmlDoc.xml;
//document.write(kmlString);
var kmlObject = earth.parseKml(kmlString);