'Creates a Google Earth/Maps "KML" file from a GroundSpeak (geocaching.com) GPX file. 'The created file is named the same as the source GPX file except for the new KML 'file extension. The source GPX file is not changed. 'Released to the Public Domain 2011 by author Eric Phelps http://ericphelps.com. 'May be used, distributed, and modified with no restrictions. This script comes with 'no warranty or expectation of usability. Just because it worked once for me on my 'test files and on my PC does not mean it will work for you on your PC and with your 'GPX files. Option Explicit Main Sub Main() Dim kml, gpx, fs, ndName, ndDescription, wpt, wpts Dim strData, elem, node, xmlerr, strXmlFile, strUrl 'Create objects Set fs = CreateObject("Scripting.FileSystemObject") Set kml = NewXmlDocObject() Set gpx = NewXmlDocObject() 'Check input If WScript.Arguments.Count <> 1 Then MsgBox "Drop a GPX file on the script!" Exit Sub End If If Not fs.FileExists(WScript.Arguments(0)) Then MsgBox "The file you dropped:" & vbCrLf & WScript.Arguments(0) & vbCrLf & "...isn't a file. Drop a GPX file on the script." Exit Sub End If If Lcase(fs.GetExtensionName(WScript.Arguments(0))) <> "gpx" Then MsgBox "The file you dropped:" & vbCrLf & WScript.Arguments(0) & vbCrLf & "...isn't a GPX file. Drop a GPX file on the script." Exit Sub End If 'Delete any existing output file strXmlFile = fs.BuildPath(fs.GetParentFolderName(WScript.Arguments(0)), fs.GetBaseName(WScript.Arguments(0)) & ".kml") If fs.FileExists(strXmlFile) Then fs.DeleteFile strXmlFile End If 'Load the file and verify it is a real GPX file gpx.load(WScript.Arguments(0)) Set node = gpx.selectSingleNode("//gpx/wpt/name") If node Is Nothing Then 'Try loading it another way strData = File2String(WScript.Arguments(0)) strData = ToAscii(strData) strData = RemoveHighBytes(strData) gpx.loadXML(strData) Set node = gpx.selectSingleNode("//gpx/wpt/name") If node Is Nothing Then MsgBox "I can't locate any named waypoints in the GPX file you dropped. The official XML parser error is:" & vbCrLf & gpx.Validate.reason Exit Sub End If End If 'Create the basic KML header kml.appendChild(kml.createProcessingInstruction("xml","version=""1.0"" encoding=""UTF-8""")) Set elem = kml.appendChild(kml.createElement("kml")) elem.SetAttribute "xmlns", "http://www.opengis.net/kml/2.2" kml.selectSingleNode("//kml").appendChild kml.createElement("Document") 'Create icon list Set elem = kml.selectSingleNode("//kml/Document").appendChild(kml.createElement("Style")) elem.SetAttribute "id", "traditional" Set node = elem.appendChild(kml.createElement("IconStyle")) Set node = node.appendChild(kml.createElement("Icon")) Set node = node.appendChild(kml.createElement("href")) node.text = "http://www.geocaching.com/images/WptTypes/2.gif" Set elem = kml.selectSingleNode("//kml/Document").appendChild(kml.createElement("Style")) elem.SetAttribute "id", "multi" Set node = elem.appendChild(kml.createElement("IconStyle")) Set node = node.appendChild(kml.createElement("Icon")) Set node = node.appendChild(kml.createElement("href")) node.text = "http://www.geocaching.com/images/WptTypes/3.gif" Set elem = kml.selectSingleNode("//kml/Document").appendChild(kml.createElement("Style")) elem.SetAttribute "id", "virtual" Set node = elem.appendChild(kml.createElement("IconStyle")) Set node = node.appendChild(kml.createElement("Icon")) Set node = node.appendChild(kml.createElement("href")) node.text = "http://www.geocaching.com/images/WptTypes/4.gif" Set elem = kml.selectSingleNode("//kml/Document").appendChild(kml.createElement("Style")) elem.SetAttribute "id", "letterbox" Set node = elem.appendChild(kml.createElement("IconStyle")) Set node = node.appendChild(kml.createElement("Icon")) Set node = node.appendChild(kml.createElement("href")) node.text = "http://www.geocaching.com/images/WptTypes/5.gif" Set elem = kml.selectSingleNode("//kml/Document").appendChild(kml.createElement("Style")) elem.SetAttribute "id", "event" Set node = elem.appendChild(kml.createElement("IconStyle")) Set node = node.appendChild(kml.createElement("Icon")) Set node = node.appendChild(kml.createElement("href")) node.text = "http://www.geocaching.com/images/WptTypes/6.gif" Set elem = kml.selectSingleNode("//kml/Document").appendChild(kml.createElement("Style")) elem.SetAttribute "id", "mystery" Set node = elem.appendChild(kml.createElement("IconStyle")) Set node = node.appendChild(kml.createElement("Icon")) Set node = node.appendChild(kml.createElement("href")) node.text = "http://www.geocaching.com/images/WptTypes/8.gif" Set elem = kml.selectSingleNode("//kml/Document").appendChild(kml.createElement("Style")) elem.SetAttribute "id", "reference" Set node = elem.appendChild(kml.createElement("IconStyle")) Set node = node.appendChild(kml.createElement("Icon")) Set node = node.appendChild(kml.createElement("href")) node.text = "http://www.geocaching.com/images/WptTypes/12.gif" Set elem = kml.selectSingleNode("//kml/Document").appendChild(kml.createElement("Style")) elem.SetAttribute "id", "parking" Set node = elem.appendChild(kml.createElement("IconStyle")) Set node = node.appendChild(kml.createElement("Icon")) Set node = node.appendChild(kml.createElement("href")) node.text = "http://www.geocaching.com/images/WptTypes/136.gif" Set elem = kml.selectSingleNode("//kml/Document").appendChild(kml.createElement("Style")) elem.SetAttribute "id", "earth" Set node = elem.appendChild(kml.createElement("IconStyle")) Set node = node.appendChild(kml.createElement("Icon")) Set node = node.appendChild(kml.createElement("href")) node.text = "http://www.geocaching.com/images/WptTypes/137.gif" Set elem = kml.selectSingleNode("//kml/Document").appendChild(kml.createElement("Style")) elem.SetAttribute "id", "wherigo" Set node = elem.appendChild(kml.createElement("IconStyle")) Set node = node.appendChild(kml.createElement("Icon")) Set node = node.appendChild(kml.createElement("href")) node.text = "http://www.geocaching.com/images/WptTypes/1858.gif" 'Process all the waypoints Set wpts = gpx.getElementsByTagName("wpt") For Each wpt In wpts 'Create the basic skeleton for a KML placemark Set node = kml.selectSingleNode("//kml/Document").appendChild(kml.createElement("Placemark")) node.AppendChild kml.createElement("name") Set ndName = node.selectSingleNode("name").appendChild(kml.createCDATASection("")) node.AppendChild kml.createElement("description") Set ndDescription = node.selectSingleNode("description").appendChild(kml.createCDATASection("")) node.appendChild kml.createElement("Point") node.selectSingleNode("Point").appendChild kml.createElement("coordinates") 'Fill in the KML name with the friendly "urlname" strData = "" On Error Resume Next strData = wpt.selectSingleNode("urlname").text On Error Goto 0 If strData <> "" Then ndName.AppendData strData End If 'Define the icon type -- if we can figure it out! strData = "" On Error Resume Next strData = wpt.selectSingleNode("type").text On Error Goto 0 If strData <> "" Then Select Case strData Case "Geocache|Traditional Cache" node.appendChild(kml.createElement("styleUrl")).text = "#traditional" Case "Geocache|Benchmark" node.appendChild(kml.createElement("styleUrl")).text = "#traditional" Case "Geocache|Letterbox Hybrid" node.appendChild(kml.createElement("styleUrl")).text = "#letterbox" Case "Geocache|Unknown Cache" node.appendChild(kml.createElement("styleUrl")).text = "#mystery" Case "Waypoint|Stages of a Multicache" node.appendChild(kml.createElement("styleUrl")).text = "#multi" Case "Geocache|Multi-cache" node.appendChild(kml.createElement("styleUrl")).text = "#multi" Case "Geocache|Earthcache" node.appendChild(kml.createElement("styleUrl")).text = "#earth" Case "Geocache|Event Cache" node.appendChild(kml.createElement("styleUrl")).text = "#event" Case "Geocache|Mega-Event Cache" node.appendChild(kml.createElement("styleUrl")).text = "#event" Case "Waypoint|Reference Point" node.appendChild(kml.createElement("styleUrl")).text = "#reference" Case "Waypoint|Parking Area" node.appendChild(kml.createElement("styleUrl")).text = "#parking" Case "Waypoint|Trailhead" node.appendChild(kml.createElement("styleUrl")).text = "#parking" Case "Geocache|Wherigo Cache" node.appendChild(kml.createElement("styleUrl")).text = "#wherigo" Case Else 'Do nothing and let it show the default icon! End Select End If 'Fill in the KML description with just about everything strUrl = "" On Error Resume Next strUrl = wpt.selectSingleNode("url").text On Error Goto 0 strData = "" On Error Resume Next strData = wpt.selectSingleNode("name").text On Error Goto 0 If strData <> "" Then If strUrl <> "" Then ndDescription.AppendData "[Details]" ndDescription.AppendData "  [Log Visit]" End If End If strData = "" On Error Resume Next strData = wpt.selectSingleNode("groundspeak:cache/groundspeak:type").text On Error Goto 0 If strData <> "" Then ndDescription.AppendData "
Type:" & strData End If strData = "" On Error Resume Next strData = wpt.selectSingleNode("groundspeak:cache/groundspeak:container").text On Error Goto 0 If strData <> "" Then ndDescription.AppendData "
Container:" & strData End If strData = "" On Error Resume Next strData = wpt.selectSingleNode("groundspeak:cache/groundspeak:difficulty").text On Error Goto 0 If strData <> "" Then ndDescription.AppendData "
Difficulty:" & strData End If strData = "" On Error Resume Next strData = wpt.selectSingleNode("groundspeak:cache/groundspeak:terrain").text On Error Goto 0 If strData <> "" Then ndDescription.AppendData "
Terrain:" & strData End If strData = "" On Error Resume Next strData = wpt.selectSingleNode("groundspeak:cache/groundspeak:short_description").text On Error Goto 0 If strData <> "" Then strData = Replace(strData, vbCr, " ") strData = Replace(strData, vbLf, " ") strData = Replace(strData, vbTab, " ") Do While Instr(strData, " ") <> 0 : strData = Replace(strData, " ", " ") : Loop strData = Trim(strData) ndDescription.AppendData "
Description: " & strData End If strData = "" On Error Resume Next strData = wpt.selectSingleNode("groundspeak:cache/groundspeak:long_description").text On Error Goto 0 If strData <> "" Then strData = Replace(strData, vbCr, " ") strData = Replace(strData, vbLf, " ") strData = Replace(strData, vbTab, " ") Do While Instr(strData, " ") <> 0 : strData = Replace(strData, " ", " ") : Loop strData = Trim(strData) ndDescription.AppendData " " & strData End If strData = "" On Error Resume Next strData = wpt.selectSingleNode("groundspeak:cache/groundspeak:encoded_hints").text On Error Goto 0 If strData <> "" Then strData = ROT13(strData) & " [" & strData & "] " strData = Replace(strData, vbCr, " ") strData = Replace(strData, vbLf, " ") strData = Replace(strData, vbTab, " ") Do While Instr(strData, " ") <> 0 : strData = Replace(strData, " ", " ") : Loop strData = Trim(strData) ndDescription.AppendData "
Hint: " & strData End If 'Add the actual coordinates strData = "" On Error Resume Next strData = wpt.getAttribute("lon") On Error Goto 0 node.selectSingleNode("Point/coordinates").Text = strData strData = "" On Error Resume Next strData = wpt.getAttribute("lat") On Error Goto 0 node.selectSingleNode("Point/coordinates").Text = node.selectSingleNode("Point/coordinates").Text & "," & strData 'node.selectSingleNode("Point/coordinates").Text = node.selectSingleNode("Point/coordinates").Text & "," & 0 Next 'Save the new KML file kml.save strXmlFile End Sub Function NewXmlDocObject() Dim xml On Error Resume Next Set xml = Nothing If xml Is Nothing Then Set xml = CreateObject("Msxml2.DOMDocument.4.0") If xml Is Nothing Then Set xml = CreateObject("Msxml2.DOMDocument") If xml Is Nothing Then Set xml = CreateObject("Msxml2.XMLDocument") If xml Is Nothing Then Set xml = CreateObject("Msxml.DOMDocument") If xml Is Nothing Then Set xml = CreateObject("Microsoft.XMLDOM") On Error Goto 0 xml.async = False Set NewXmlDocObject = xml End Function Function HtmlEncode(strText) 'Because it is NEVER safe to display data that has been influenced by a user. Dim strBuffer, lngPos strBuffer = "" If Not IsNull(strText) Then If strText <> "" Then For lngPos = 1 To Len(strText) strBuffer = strBuffer & "&#" & Asc(Mid(strText, lngPos, 1)) & ";" Next End If End If HtmlEncode = strBuffer End Function Function File2String(strFile) 'As String Dim fs 'As Scripting.FileSystemObject Dim ts 'As Scripting.TextStream Const ForReading = 1 Set fs = CreateObject("Scripting.FileSystemObject") If fs.FileExists(strFile) Then Set ts = fs.OpenTextFile(strFile, ForReading, True) If ts.AtEndOfStream Then File2String ="" Else File2String = ts.ReadAll End If ts.Close Else File2String = "" End If End Function Function RemoveHighBytes(strText) Dim lngLength, lngCount, strOut strOut = "" For lngCount = 1 To Len(strText) If Asc(Mid(strText, lngCount, 1)) > 127 Then strOut = strOut & " " Else strOut = strOut & Mid(strText, lngCount, 1) End If Next RemoveHighBytes = strOut End Function Function ToAscii(strText) 'Replaces unusual characters with plain ascii Dim strGood, strBad, strContent strContent = strText strGood = """" : For Each strBad In Split("“ ”") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "--" : For Each strBad In Split("– —") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "ss" : For Each strBad In Split("ß") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "'" : For Each strBad In Split("’ ‘ ` ´") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "A" : For Each strBad In Split("Ä Å À Á Â Ã") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "C" : For Each strBad In Split("Ç") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "E" : For Each strBad In Split("Ë È É Ê") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "I" : For Each strBad In Split("Í Î Ï Ì") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "N" : For Each strBad In Split("Ñ") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "O" : For Each strBad In Split("Ø Ó Ô Õ Ö Ò") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "U" : For Each strBad In Split("Ü Û Ú Ù") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "Y" : For Each strBad In Split("Ý") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "a" : For Each strBad In Split("å ä ã â á à") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "c" : For Each strBad In Split("ç") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "e" : For Each strBad In Split("ë ê é è") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "i" : For Each strBad In Split("ï î í ì") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "n" : For Each strBad In Split("ñ") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "o" : For Each strBad In Split("ø ö õ ô ó ò") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "u" : For Each strBad In Split("ü û ú ù") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "y" : For Each strBad In Split("ÿ ý") : strContent = Replace(strContent, strBad, strGood) : Next strGood = "z" : For Each strBad In Split("ž") : strContent = Replace(strContent, strBad, strGood) : Next ToAscii = strContent End Function Function ROT13(strString) 'As String 'ROT13 substitutes each letter for one that is 13 characters 'further in the alphabet. "A" becomes "N", "B" becomes "O", 'and so on. This is a symetrical code (encoding and decoding 'by the same function) Const DOUBLE_ALPHABET = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ" Dim strROT13 Dim lngPos Dim intChar For lngPos = 1 To Len(strString) intChar = Instr(DOUBLE_ALPHABET, Mid(strString, lngPos, 1)) If intChar = 0 Then strROT13 = strROT13 & Mid(strString, lngPos, 1) Else strROT13 = strROT13 & Mid(DOUBLE_ALPHABET, intChar + 13, 1) End If Next ROT13 = strROT13 End Function