module Birdwatcher # KML Document generator # # KML is a file format used to display geographic data in an Earth browser # such as Google Earth. You can create KML files to pinpoint locations, add # image overlays, and expose rich data in new ways. KML is an international # standard maintained by the Open Geospatial Consortium, Inc. (OGC). # # This class supports generating basic KML documents with Placemarks and Folders. # # @note Attribute values ARE NOT automatically escaped. All values will have to be given in an HTML escaped fashion if there is a risk that they might contain unexpected or dangerous HTML. # @see https://developers.google.com/kml/ class KML # KML document header DOCUMENT_HEADER = <<-HEAD HEAD # KML document footer DOCUMENT_FOOTER = <<-FOOT FOOT class Error < StandardError; end class UnknownFolderError < Birdwatcher::KML::Error; end # Class initializer # # @param attributes [Hash] Document attributes # @see https://developers.google.com/kml/documentation/kmlreference#document def initialize(attributes = {}) @attributes = attributes @folders = {} @placemarks = [] end # Add a Placemark # # @param attributes [Hash] Placemark attributes # @see https://developers.google.com/kml/documentation/kmlreference#placemark def add_placemark(attributes) @placemarks << attributes end # Add a Folder # # @param id [String] Folder ID # @param attributes [Hash] Folder attributes # # @see https://developers.google.com/kml/documentation/kmlreference#folder def add_folder(id, attributes) @folders[id] = { :placemarks => [] }.merge(attributes) end # Add a Placemark to a Folder # # @param folder_id [String] # @param attributes [Hash] Placemark attributes # # @raise [Birdwatcher::KML::UnknownFolderError] if folder doesn't exist # @see https://developers.google.com/kml/documentation/kmlreference#placemark def add_placemark_to_folder(folder_id, attributes) fail(UnknownFolderError, "There is no folder with id: #{folder_id}") unless @folders.key?(folder_id) @folders[folder_id][:placemarks] << attributes end # Generate the KML document # # @return the final KML document def generate output = generate_document_header @folders.each_pair { |id, attributes| output += generate_folder(id, attributes) } output += @placemarks.map { |p| generate_placemark(p) }.join output += generate_document_footer end private # Generate document header # @private def generate_document_header header = DOCUMENT_HEADER @attributes.each_pair { |k, v| header += "<#{k}>#{escape(v)}\n" } header end # Generate document footer # @private def generate_document_footer DOCUMENT_FOOTER end # Generate Placemark element # @private def generate_placemark(attributes) placemark = attributes.key?(:id) ? "" : "" attributes.delete(:id) attributes.each_pair { |k, v| placemark += "<#{k}>#{v}\n" } placemark += "\n" end # Generate Folder element # @private def generate_folder(id, attributes) placemarks = attributes.delete(:placemarks) folder = "" attributes.each_pair { |k, v| folder += "<#{k}>#{escape(v)}\n" } folder += placemarks.map { |p| generate_placemark(p) }.join folder += "\n" end # HTML escape a string # @private def escape(string) CGI.escapeHTML(string.to_s) end end end