#! /usr/bin/env ruby
require "nokogiri"
xml = <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<name>Document.kml</name>
<Placemark>
<name>Waypoint</name>
<Point>
<coordinates>-122.371,37.816,0</coordinates>
</Point>
</Placemark>
<Placemark>
<name>Track</name>
<LineString>
<coordinates>-0.376291,43.296237,199.75 -0.376299,43.296237,199.75</coordinates>
</LineString>
</Placemark>
</Document>
</kml>
EOF
doc = Nokogiri::XML(xml)
# 1. insert/replace the document's name.
#
# First, let's find the name node.
# For explanation of why the "xmlns" is needed, check out:
# > https://nokogiri.org/tutorials/searching_a_xml_html_document.html#namespaces
#
name_node = doc.at_xpath("/xmlns:kml/xmlns:Document/xmlns:name")
#
# You could also use a CSS query which will (mostly) ignore namespaces. This is exactly the same search.
#
name_node = doc.at_css("kml > Document > name")
#
# Modify the contents of the <name/> node
#
name_node.content = "New Document Name"
# 2. a. find all Placemark blocks that contain a LineString
#
# I can think of two ways to do this. The first way is to search for
# LineString nodes within a Placemark node, and then get those nodes' parents:
placemarks_with_linestring = doc.xpath("//xmlns:Placemark/xmlns:LineString").map(&:parent)
# This first approach would work with CSS as well:
placemarks_with_linestring = doc.css("Placemark > LineString").map(&:parent)
# The second approach is to just use an XPath query to express that
# you want Placemarks that contain a LineString:
placemarks_with_linestring = doc.xpath("//xmlns:Placemark[xmlns:LineString]")
# Then you can add a new child node to that Placemark:
placemarks_with_linestring.each do |placemark|
# the string passed into add_child is parsed just like any other XML fragment
placemark.add_child "<some>blah</some>"
end
# The end result:
puts doc.to_xml
# >> <?xml version="1.0" encoding="UTF-8"?>
# >> <kml xmlns="http://www.opengis.net/kml/2.2">
# >> <Document>
# >> <name>New Document Name</name>
# >> <Placemark>
# >> <name>Waypoint</name>
# >> <Point>
# >> <coordinates>-122.371,37.816,0</coordinates>
# >> </Point>
# >> </Placemark>
# >> <Placemark>
# >> <name>Track</name>
# >> <LineString>
# >> <coordinates>-0.376291,43.296237,199.75 -0.376299,43.296237,199.75</coordinates>
# >> </LineString>
# >> <some>blah</some></Placemark>
# >> </Document>
# >> </kml>
# 3. Write this to a file
#
# Use normal Ruby idioms for opening a file and writing to it, and use #to_xml to serialize the doc:
File.open("output.kml", "w") do |file|
file.write doc.to_xml
end
and the variation in response to the question "why is the new node not lined up?"
doc = Nokogiri::XML(xml) do |config|
config.noblanks
end
and another variation on how to add the new node, using Builder.with functionality:
placemarks_with_linestring.each do |placemark|
Nokogiri::XML::Builder.with(placemark) do |placemark|
placemark.some "blah"
end
end
Answer to nokogiri-talk thread Simple example to read+edit KML files
It's a good example of:
and the variation in response to the question "why is the new node not lined up?"
and another variation on how to add the new node, using
Builder.with
functionality: