tryxmldom.rex source

00001 /**
00002   Just a little program to excercise (some of ) the xmlDOM classes.
00003  
00004   Transforms the XML result of a google maps directions query such as:
00005   <pre>
00006   http://maps.googleapis.com/maps/api/directionsxml?origin=Uithoorn,NL&destination=Nieuw+Vennep,NL&mode=bicycling
00007   </pre>
00008   into a GPX file, that can be downloaded/used on smartphones/tablets with apps such as Viewranger, OSMAnd+ and MyTrails.
00009   @param xmlFile - the name of the xml-file to proces
00010   @return gpxStream - The GPX file to STDOUT (,which can be redirected to a file of course).
00011 */
00012 parse arg xmlFile
00013 signal on user domException name domException
00014 --trace i
00015 xmlStream = .stream~new(xmlFile)
00016 parser = .xmlParser~new(.dom1Builder~new)
00017 dom = parser~parseStream(xmlStream)
00018 -- One leg from Google is treated as one GPX track
00019 doc = dom~documentElement
00020 -- Use the travel mode as content for the <type> tag in GPX <rte> and <trk> elements
00021 travel_mode = doc~getElementsByTagName("travel_mode")~item(0)~firstChild~nodeValue
00022 -- Google requires  to show the following 2 items
00023 copyrights = doc~getElementsByTagName("copyrights")
00024 warnings = doc~getElementsByTagName("warning")
00025 -- Retrieve from and to address
00026 startAddress = doc~getElementsByTagName("start_address")~item(0)~firstChild~nodeValue
00027 endAddress = doc~getElementsByTagName("end_address")~item(0)~firstChild~nodeValue
00028 -- Get the latitude and longitude boundaries
00029 bounds = doc~getElementsByTagName("bounds")~item(0)
00030 maxlat = bounds~getElementsByTagName("northeast")~item(0)~childNodes~item(0)~firstChild~nodeValue
00031 maxlon = bounds~getElementsByTagName("northeast")~item(0)~childNodes~item(1)~firstChild~nodeValue
00032 minlat = bounds~getElementsByTagName("southwest")~item(0)~childNodes~item(0)~firstChild~nodeValue
00033 minlon = bounds~getElementsByTagName("southwest")~item(0)~childNodes~item(1)~firstChild~nodeValue
00034 -- Now process the route(s) info, assumption is here just 1 leg
00035 legs = doc~getElementsByTagName("route")~item(0)~getElementsByTagName("leg")
00036 do l=0 to legs~length-1
00037   leg = legs~item(l)
00038   legData = .directory~new
00039   legData~polyLines = .array~new
00040   legData~directions = .array~new
00041   legData~routePoints = .array~new
00042   legData~distances = .array~new
00043   legData~durations = .array~new
00044   -- get the encoded polyline strings for each step in this leg
00045   encodedPolylines = leg~getElementsByTagName("polyline")
00046   -- get the driving instructions for each step in this leg
00047   stepDirections = leg~getElementsByTagName("html_instructions")
00048   -- get the latitude and longitude for each step start location in this leg
00049   stepStartLocations = leg~getElementsByTagName("start_location")
00050   -- get the distance for each step in this leg
00051   stepDistances = leg~getElementsByTagName("distance")
00052   -- get the theoretical duration for each step in this leg
00053   stepDurations = leg~getElementsByTagName("duration")
00054   -- now process each step
00055   steps = leg~getElementsByTagName("step")
00056   do s=0 to steps~length-1
00057     -- decode the Google encoded polylin string
00058     encPoly = encodedPolylines~item(s)~firstChild~firstChild~nodeValue
00059     polyline = decodePoly(encPoly)
00060     -- and add to the polyline array
00061     legData~polyLines~append(polyLine)
00062     -- add the directions for each step
00063     legData~directions~append(stepDirections~item(s)~firstChild~nodeValue)
00064     -- add latitude longitude info for each turning point in this leg
00065     lat = stepStartLocations~item(s)~childNodes~item(0)~firstChild~nodeValue
00066     lon = stepStartLocations~item(s)~childNodes~item(1)~firstChild~nodeValue
00067     legData~routePoints~append(lat lon)
00068     -- append the distance and duration info for each step
00069     legData~distances~append(stepDistances~item(s)~childNodes~item(1)~firstChild~nodeValue)
00070     legData~durations~append(stepDurations~item(s)~childNodes~item(0)~firstChild~nodeValue)
00071   end
00072 end
00073 out = .xmlDOMImplementation~new~createDocument
00074 -- append the retrieved xml PI as first child
00075 out~appendChild(dom~firstChild)
00076 -- create the gpx-tag and its attributes
00077 out~appendChild(out~createElement("gpx"))
00078 gpx = out~childNodes~item(1)
00079 attr = out~createAttribute("version", "1.1")
00080 attr~ownerElement = gpx
00081 gpx~setAttributeNode(attr)
00082 attr = out~createAttribute("creator", "rji@xs4all.nl")
00083 attr~ownerElement = gpx
00084 gpx~setAttributeNode(attr)
00085 attr = out~createAttribute("xmlns", "http://www.topografix.com/GPX/1/1")
00086 attr~ownerElement = gpx
00087 gpx~setAttributeNode(attr)
00088 attr = out~createAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
00089 attr~ownerElement = gpx
00090 gpx~setAttributeNode(attr)
00091 attr = out~createAttribute("xsi:schemaLocation","http://www.topografix.com/GPX/1/1/gpx.xsd")
00092 attr~ownerElement = gpx
00093 gpx~setAttributeNode(attr)
00094 -- handle the gpx metadata
00095 metadata = gpx~appendChild(out~createElement("metadata"))
00096 -- First the copyright notice(s)
00097 text = ''
00098 do i=0 to copyrights~length-1
00099   text ||= copyrights~item(i)~firstChild~nodeValue || .endofline
00100 end
00101 text = text~substr(1,text~length-2)  
00102 textNode = out~createTextNode(text)
00103 copyright = out~createElement("copyright")
00104 copyright~appendChild(textNode)
00105 metadata~appendChild(copyright)
00106 -- Second the author info
00107 author = out~createElement("author")
00108 name = out~createElement("name")
00109 text = out~createTextNode("Ruurd J. Idenburg")
00110 name~appendChild(text)
00111 author~appendChild(name)
00112 link = out~createElement("link")
00113 attr = out~createAttribute("href", "http://www.idenburg.net")
00114 attr~ownerElement = link
00115 link~setAttributeNode(attr)
00116 author~appendChild(link)
00117 metadata~appendChild(author)
00118 -- Third the description, i.e the warning(s) 
00119 desc = out~createElement("desc")
00120 text = ''
00121 do i=0 to warnings~length-1
00122   text ||= warnings~item(i)~firstChild~nodeValue || .endofline
00123 end
00124 if text~length>0 then text = text~substr(1,text~length-2) 
00125 textNode = out~createTextNode(text)
00126 desc~appendChild(textNode)
00127 metadata~appendChild(desc)
00128 -- Fourth the name, in this case the input filename
00129 name = out~createElement("name")
00130 text = out~createTextNode(xmlStream~string)
00131 name~appendChild(text) 
00132 metadata~insertBefore(name,metadata~childNodes~item(0))
00133 -- Fifth the keywords, in this case the travel_mode
00134 keywords = out~createElement("keywords")
00135 text = out~createTextNode(travel_mode)
00136 keywords~appendChild(text)
00137 metadata~appendChild(keywords)
00138 -- Sixth the geographical bounds of the route/track
00139 bounds = out~createElement("bounds")
00140 attr = out~createAttribute("minlat", minlat)
00141 attr~ownerElement = bounds
00142 bounds~setAttributeNode(attr)
00143 attr = out~createAttribute("minlon", minlon)
00144 attr~ownerElement = bounds
00145 bounds~setAttributeNode(attr)
00146 attr = out~createAttribute("maxlat", maxlat)
00147 attr~ownerElement = bounds
00148 bounds~setAttributeNode(attr)
00149 attr = out~createAttribute("maxlon", maxlon)
00150 attr~ownerElement = bounds
00151 bounds~setAttributeNode(attr)
00152 metadata~appendChild(bounds)
00153 -- gpx metadata is handled, now process the route (rte-tag) 
00154 rte = out~createElement("rte")
00155 -- append route as next child to gpx-tag
00156 gpx~appendChild(rte)
00157 -- the name of the route
00158 name = out~createElement("name")
00159 rte~appendChild(name)
00160 text = out~createTextNode(startAddress"-"endAddress)
00161 name~appendChild(text)
00162 -- type is travel_mode
00163 type = out~createElement("type")
00164 rte~appendChild(type)
00165 text = out~createTextNode(travel_mode)
00166 type~appendChild(text)
00167 -- Now do the route points and driving instructions
00168 do i=1 to legData~routePoints~items
00169   routePoint = legData~routePoints[i]
00170   rtept = out~createElement("rtept")
00171   -- latitude and longitude are attributes 
00172   attr = out~createAttribute("lat", routePoint~word(1))
00173   attr~ownerElement = rtept
00174   rtept~setAttributeNode(attr)
00175   attr = out~createAttribute("lon", routePoint~word(2))
00176   attr~ownerElement = rtept
00177   rtept~setAttributeNode(attr)
00178   rte~appendChild(rtept)
00179   -- cmt-tag contains driving instructions and distance to next turning point
00180   cmt = out~createElement("cmt")
00181   text = out~createTextNode(doEntities(legData~directions[i])"; Go" legData~distances[i]"." )
00182   cmt~appendChild(text)
00183   rtept~appendChild(cmt)
00184 end
00185 -- Route info handled now do the track(s)
00186 -- Create the trk-tag as next child of gpx-tag
00187 trk = out~createElement("trk")
00188 gpx~appendChild(trk)
00189 -- track name 
00190 name = out~createElement("name")
00191 trk~appendChild(name)
00192 text = out~createTextNode(startAddress"-"endAddress)
00193 name~appendChild(text)
00194 -- track type,i.e travel_mode
00195 type = out~createElement("type")
00196 trk~appendChild(type)
00197 text = out~createTextNode(travel_mode)
00198 type~appendChild(text)
00199 -- a track can have multiple track segments
00200 trkseg = out~createElement("trkseg")
00201 trk~appendChild(trkseg)
00202 -- do the track points
00203 do i=1 to legData~polyLines~items
00204   -- each polyline is an array of lat lon pairs
00205   polyLine = legData~polyLines[i]
00206   do j=1 to polyLine~items
00207     -- each track point is a lat lon pair
00208     trackPoint = polyLine[j]
00209     trkpt = out~createElement("trkpt")
00210     attr = out~createAttribute("lat", trackPoint~word(1))
00211     attr~ownerElement = trkpt
00212     trkpt~setAttributeNode(attr)
00213     attr = out~createAttribute("lon", trackPoint~word(2))
00214     attr~ownerElement = trkpt
00215     trkpt~setAttributeNode(attr)
00216     trkseg~appendChild(trkpt)
00217   end
00218 end
00219 -- End of GPX DOM build, now create the GPX file
00220 call domWriter out~childNodes, .queue~new
00221 exit
00222  
00223 /**
00224   Encodes entities for xml text
00225   @param text - the text with plain entities 
00226   @return text - the encoded text 
00227 */
00228 doEntities: Procedure
00229   parse arg text
00230   text = text~changeStr('&',"&amp;") -- must be first
00231   text = text~changeStr('<',"&lt;")
00232   text = text~changeStr('>',"&gt;")
00233   text = text~changeStr("'","&apos;")
00234   text = text~changeStr('"',"&quot;")
00235 return text
00236 /**
00237   Walks the DOM tree recursively and generates the GPX tags and their contents
00238   @param nodes - a xmlNodeList to start the treewalk
00239   @param tagStack - a .queue instance to keep track of nested tags
00240   @return gpxStream - the gpx contents to STDOUT
00241 */
00242 ::routine domWriter
00243   use strict arg nodes, tagStack
00244   indent = tagStack~items*2
00245   do n=0 to nodes~length-1
00246     node = nodes~item(n)
00247     select
00248       -- 1 elementNode
00249       when node~nodeType=.xmlNode~elementNode then do
00250         tag = node~tagName
00251         attributes = ''
00252         if node~attributes~length>0 then do
00253           attrDir = node~attributes~toOorexxDirectory
00254           do i over attrDir
00255             attrName = attrDir[i]~name
00256             attrValue = attrDir[i]~value
00257             delimiter = '"'
00258             if value~pos('"')>0 then delimiter = "'"
00259             if (delimiter='"') 
00260               then attributes = attributes attrName'="'attrValue'"'
00261               else attributes = attributes attrName"='"attrValue"'"
00262           end
00263         end
00264         if attributes~length>0 then attributes = ' 'attributes
00265         if node~childNodes~length>0 then do
00266           say ' '~copies(indent) || '<'tag || attributes || '>'
00267           tagStack~push(tag)
00268           call domWriter node~childNodes, tagStack
00269         end
00270         else do
00271           say ' '~copies(indent) || '<'tag || attributes || "></" || tag || '>'
00272          end
00273       end 
00274       -- 2 attributeNode is handled above in elementNode
00275       -- 3 textNode
00276       when node~nodeType=.xmlNode~textNode then do
00277         say ' '~copies(indent) || node~data
00278       end
00279       -- 4 CDATASectionNode
00280       when node~nodeType=.xmlNode~cdataSectionNode then do
00281         say ' '~copies(indent) || "<![CDATA[" || node~data || "]]>"
00282       end
00283       -- 5 entityReferenceNode
00284       when node~nodeType=.xmlNode~entityReferenceNode then do
00285         nop
00286       end
00287       -- 6 entityNode
00288       when node~nodeType=.xmlNode~entityNode then do
00289         nop
00290       end
00291       -- 7 PINode
00292       when node~nodeType=.xmlNode~processingInstructionNode then do
00293         target = node~target
00294         data = node~data
00295         say ' '~copies(indent) || "<?"target data"?>"
00296       end
00297       -- 8 commentNode
00298       when node~nodeType=.xmlNode~commentNode then do
00299         data = node~data
00300         say ' '~copies(indent) || "<!--"data"-->"
00301       end
00302       -- 9 documentNode handled by the caller
00303       -- 10 documentTypeNode
00304       when node~nodeType=.xmlNode~documentTypeNode then do
00305         nop
00306       end
00307       -- 11 documentFragmentNode
00308       when node~nodeType=.xmlNode~documentFragmentNode then do
00309         nop
00310       end
00311       -- 12 notationNode
00312       when node~nodeType=.xmlNode~notationNode then do
00313         nop
00314       end
00315       otherwise do
00316         --say '====================='node~nodeType
00317         nop
00318       end
00319     end
00320   end
00321   if tagStack~items>0 then do
00322     tag = tagStack~pull
00323     indent = tagStack~items*2
00324     say ' '~copies(indent) || "</" || tag || '>'
00325   end
00326 return
00327 /**
00328   Exception catcher
00329 */
00330 domException: 
00331 say condition('A')
00332 say condition('D')
00333 exit
00334  
00335 ::requires 'xmlDOM.cls'

Get RexxLiterate at SourceForge.net. Fast, secure and Free Open Source software downloads
Generated on 30 Jul 2015 13:56:48 for xmlDOM for OOrexx by rexxliterate  0.0.1