Search code examples
xmlxpathvbscriptkml

skip certain nodes using XPath in a VBS file


I wish to hop past certain nodes but can not figure it out. My objective is to

  • traverse the KML file, stop at the first [SimpleData name="POA_2006"] node and write the value to a log. This all good.
  • the hop into the first [coordinates] node and log all the coordinates. This is all good.
  • now I wish to skip any other [coordinates] nodes that may be in the particular section/postcode. This is not happening. How do I do this?

Here is a very cut down version of my KML file:

   <?xml version="1.0" encoding="utf-8" ?>
    <kml xmlns="http://www.opengis.net/kml/2.2">
    <Document id="root_doc">
    <Schema name="postcode" id="postcode">
        <SimpleField name="STATE_2006" type="string"></SimpleField>
    <SimpleField name="POA_2006" type="string"></SimpleField>
    </Schema>
    <Folder><name>postcode</name>
      <Placemark>
        <Style><LineStyle><color>ff0000ff</color></LineStyle><PolyStyle><fill>0</fill></PolyStyle></Style>
        <ExtendedData><SchemaData schemaUrl="#postcode">
            <SimpleData name="STATE_2006">1</SimpleData>
            <SimpleData name="POA_2006">2000</SimpleData>
        </SchemaData></ExtendedData>
          <MultiGeometry><Polygon><altitudeMode>relativeToGround</altitudeMode><outerBoundaryIs><LinearRing><altitudeMode>relativeToGround</altitudeMode>
          <coordinates>151.20118275199999,-33.873293252 151.201538016</coordinates></LinearRing></outerBoundaryIs></Polygon><Polygon><altitudeMode>relativeToGround</altitudeMode><outerBoundaryIs><LinearRing><altitudeMode>relativeToGround</altitudeMode>
          <coordinates>151.225379008,-33.855265002 151.22524198400001</coordinates></LinearRing></outerBoundaryIs></Polygon></MultiGeometry>
      </Placemark>
      <Placemark>
        <Style><LineStyle><color>ff0000ff</color></LineStyle><PolyStyle><fill>0</fill></PolyStyle></Style>
        <ExtendedData><SchemaData schemaUrl="#postcode">
            <SimpleData name="STATE_2006">1</SimpleData>
            <SimpleData name="POA_2006">2006</SimpleData>
        </SchemaData></ExtendedData>
          <Polygon><altitudeMode>relativeToGround</altitudeMode><outerBoundaryIs><LinearRing><altitudeMode>relativeToGround</altitudeMode><coordinates>151.182640768,-33.891296046 151.18194374399997</coordinates></LinearRing></outerBoundaryIs></Polygon>
      </Placemark>
    </Folder>
    </Document></kml>

and her is the VBS file code:

    function readKLMFileAndWriteToFile2(toFileName, fromFileName)
      Dim fso, fPath ,xXML   
      Set fso  = CreateObject("Scripting.FileSystemObject")
      fPath    = fso.GetAbsolutePathName(fromFileName)
      Set xXML = CreateObject("Msxml2.DOMDocument")
      xXML.SetProperty "SelectionNamespaces", "xmlns:base=""http://www.opengis.net/kml/2.2"""
      xXML.setProperty "SelectionLanguage", "XPath"
      xXML.async = False
      xXML.load fPath

      dim simpleData, coords, ctr
      ctr = 0
      For Each simpleData In xXML.selectNodes("//base:SimpleData[@name='POA_2006']")
          writeLog simpleData.text
          set coords = xXML.selectNodes("//base:coordinates")
          writeLog coords(ctr).text
          ctr = ctr + 1
      Next
    end function 

Note - This file does not error in any way. The issue is the set of coords in the second [coordinates] node inside the first postcode (2000) populates its self in where the coordinates should be for the next postcode (2006). So how can I skip this second [coordinates] node? I figure inside my existing For each SimpleData loop I need to do something tricky such as detect how many [coordinates] nodes are within some common nodes such as [outerBoundaryIs] if there are two like in postcode 2000 I need to add this number to my ctr variable. This logic does work because I can hard code this and get the result but Im a bit stuck doing it dynamically. Thank you in advance for help

UPDATE - Thank you for your input. I have removed the unnecessary coords from my example KLM file above so it easier to read. Based on this change, as requested by @Mathias Müller here is the intended output (postCode, then the contents in the first coordinated node, then jump to next postcode, then the contents in the first coordinated node in this new postcode ) :

2000
151.20118275199999,-33.873293252 151.201538016
2006
151.182640768,-33.891296046 151.18194374399997

the suggestions of set coords = xXML.selectNodes("//base:coordinates[1]") did not work.

I also tried the suggested FOR loop. The code does not like (object expected):

set coord = simpleData.selectSingleNode("./ancestor::base:ExtendedData/following-sibling::base:MultiGeometry//base:coordinates")

Solution

  • Apologies for answering my own question. I thought the solution may get lost in my long post. Thank you @har07 for leading me to this page about XPath expresions. This was where my answer was but also I realised refering to my document (xXML) inside the loop was no good. I wonder if a solution was even possible this way. Replace the for loop in my initial post with this for the solution:

      For Each Placemark In xXML.selectNodes("//base:Placemark")
          set simpleData = Placemark.selectNodes("//base:SimpleData[@name='POA_2006']")
          writeLog simpleData(ctr).text
          set coords = Placemark.selectNodes(".//base:coordinates")
          writeLog coords(0).text 
          ctr = ctr +  1
      Next