HomeMathComputingArtsWordsLiteratureMusictwitter facebook webfeed

Emacs Lisp: Writing a google-earth Function

Advertise Here For Profit

Xah Lee, 2006-12, 2010-07-25

This page shows a example of writing a emacs lisp command that creates a Google Earth KML file of a given location, and creates a link to the file. If you don't know elisp, first take a look at Emacs Lisp Basics.

If you don't know what Google Earth is, see: Google Earth.

The Problem

Summary

I want to write a command so that if my cursor is on a line such as this:

Las Vegas,-115.1730,36.1027

After pressing a button, the line will become:

<a href="../kml/las_vegas.kml" title="Las Vegas">⊕</a>

And a Google Earth KML file “Las_Vegas.kml” will be automatically created, linked by the above.

Detail

I often write travelogs on my website. If i traveled to Las Vegas, then my Las Vegas travelog page will have a link to Google Earth location of Las Vegas. The raw html looks like this:

<a href="../kml/las_vegas.kml" title="Las Vegas">⊕</a>

with proper Cascading Style Sheet (CSS), like this:

a[href$=".kml"], a[href$=".kmz"]
{background:url(img/google_earth_link.png)
no-repeat left center;
padding-left:25px;
background-position:left center !important}

it looks like this in a web browser:

If you move cursor to the link, it will show the title. Clicking on it will open the KML file and launch Google Earth to that location.

Also, i want emacs to automatically create the KML file for me. The KML file content would be like this:

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Placemark>
<name>Las Vegas</name>
<description>
See: http://xahlee.info/Periodic_dosage_dir/las_vegas/20031020_vegas.html
</description>
<Point><coordinates>-115.1730,36.1027</coordinates></Point>
</Placemark>
</kml>

In the “<description>” tag, contains the URL of my web page that links to this KML file. So that in Google Earth, user can easily get back to my travelog page.

In summary, given a location name and its earth coordinates, i want emacs automatically create a KML file, and a link to the KML file. We proceed to write this function.

Solution

First, we write a documentation for the function so that we know precisely what we want.

(defun make-google-earth ()
"Create a KML file and replace the current line as a link to it.

The current line must have data of this format:
‹Name›,‹longitude›,‹latitude›

Example:
Las Vegas,-115.1730,36.1027

The line will be replaced like this:
<a href=\"../kml/las_vegas.kml\" title=\"Las Vegas\">⊕</a>

The KML file will be created in this dir:
〔~/web/xahlee_org/kml/〕."
(interactive)
;; )

Here are the major steps we need to do.

To grab the current line and set it to variables, we can easily do like this:

  (setq p1 (line-beginning-position))
  (setq p2 (line-end-position))

  (setq dataText (buffer-substring-no-properties p1 p2 ))
  (setq dataTextTempList (split-string dataText ","))
  (setq title (pop dataTextTempList))
  (setq xcoord (pop dataTextTempList))
  (setq ycoord (pop dataTextTempList))

Once we got the data, we can delete the line, like this:

(delete-region p1 p2)

For inserting the link, we can write it like this:

(defun insert-google-earth-link (&optional title filePath)
  "Insert a HTML markup for link to local Goole Earth file.
 TITLE is the “title” attribute in the link.
 FILE-PATH is the path to the KML file.
Here's a sample inserted text:
<a href=\"../kml/las_vegas.kmz\" title=\"Las Vegas\">⊕</a>"
  (interactive)
  (let ()
    (when (not title) (setq title "ttt") )
    (when (not filePath) (setq filePath "ttt") )
    (insert "<a href=\"" filePath "\" title=\"" title "\">⊕</a>\n")
    ))

To create a KML file, we can call “find-file”, then just insert a template text. Like this:

(find-file kmlFilePath)
(insert-kml title xcoord ycoord sourceFilePath)

The “insert-kml” can be written like this:

(defun insert-kml (&optional title xcoord ycoord sourceFilePath)
  "Insert a simple Google Earth KML markup template.
 TITLE is the name to use for the <name> tag.
 XCOORD is the longitude.
 YCOORD is the lattitude.
 SOURCEFILEPATH is the file that links to this kml file,
used in the <description> (full path)"
  (interactive)
  (let ()
    (when (not title) (setq title "ttt" xcoord "ttt" ycoord "ttt"))
    (insert "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<kml xmlns=\"http://www.opengis.net/kml/2.2\">
<Placemark>
<name>" title "</name>
<description>

See: http://xahlee.info/" 
(file-relative-name sourceFilePath "~/web/xahlee_org/" )
  "
</description>
<Point><coordinates>" xcoord "," ycoord "</coordinates></Point>
</Placemark>
</kml>\n")))

Complete Code

Here's the final make-google-earth code:

(defun make-google-earth ()
"Create a KML file and replace the current line as a link to it.

The current line must have data of this format:
‹Name›,‹longitude›,‹latitude›

Example:
Las Vegas,-115.1730,36.1027

The line will be replaced like this:
<a href=\"../kml/las_vegas.kml\" title=\"Las Vegas\">⊕</a>

The KML file will be created in this dir:
〔~/web/xahlee_org/kml/〕."
(interactive)
(let (
      title xcoord ycoord
      dataText dataTextTempList
      kmlFilePath kmlDirRoot 
      sourceFilePath doit-p
      p1 p2
      )
  (setq p1 (line-beginning-position))
  (setq p2 (line-end-position))
  (setq dataText (buffer-substring-no-properties p1 p2 ))

  (setq kmlDirRoot "~/web/xahlee_org/kml/")
  (setq dataTextTempList (split-string dataText ","))
  (setq title (pop dataTextTempList))
  (setq xcoord (pop dataTextTempList))
  (setq ycoord (pop dataTextTempList))
  (setq sourceFilePath buffer-file-name)
  (setq kmlFilePath (concat (file-relative-name kmlDirRoot )
                            (replace-regexp-in-string " " "_" title) ".kml"))

  (setq doit-p t)

  (when (file-exists-p kmlFilePath)
    (setq doit-p nil)
    (setq doit-p (y-or-n-p (format "File exist at %s\nDo you want to replace it?" kmlFilePath)))
    )

  (when doit-p
    ;; (insert-google-map-link title latlon)
    (delete-region p1 p2)
    (insert-google-earth-link title kmlFilePath)
    (find-file kmlFilePath)
    (erase-buffer)
    (insert-kml title xcoord ycoord sourceFilePath)
    (search-backward "<description>") (forward-char 14)
    (nxml-mode)
    (save-buffer)
    )
  ))

So now, we have a elisp function, that we can assign to a keystroke. Upon a press of button, it saves us a few hundreds of tedious error-prone keystrokes and file managing process.

Emacs is fantastic.

For KML files i created on my site with this function, see: Google Earth Files at XahLee.org.

blog comments powered by Disqus