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.
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.
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.
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")))
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.