|
Tutorial
How to use happy-kml-tool
IntroductionThe basic premise of using happy-kml-tool is that the command-line utility, hkt, reads a KML file that contains special markup that identifies data you would like to pull into the output kml file as well as a CSS-like stylesheet that you want applied to the output. The resulting KML file contains none of this special markup, but transforms those instructions into valid KML tags to achieve the results you want. The Basicshkt expects a KML file, and is perfectly happy to read the following: ./sample.kml
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2"
xmlns:gx="http://www.google.com/kml/ext/2.2"
xmlns:hkt="http://code.google.com/p/happy-kml-tool/wiki/Reference">
<Document>
<name>Marine Mammals</name>
</Document>
</kml>
To process the file, run the following from the command line: $ htk ./sample.kml -o ./output.kml generated 1 kml file warning - no hkt:Placemarks or hkt:Style tags found. Of course, hkt is just going to output the contents of sample.kml since there are no style instructions or data sources specified to add to the file. The only difference from your average kml file is the xmlns:hkt declaration at the top of the page, this allows us to add hkt:Placemarks and hkt:Style tags. hkt will generate more useful summary statistics after generating more complex files. The command line utilities are also capable of looking at your data and suggesting performance optimizing style rules. See the CommandlineUtilities wiki for more information. Adding DataThe hkt:Placemarks tag can be used to pull data from any source that can be read by OGR. It converts each Feature in the specified dataset into a Placemark, which by default is represented by the default yellow pushpin icon for points or yellow linestrings and polygons. So lets say we have a shapefile containing the locations of all pinnipeds rookeries (Seals and Sea Lions) in California. To add it to our document, we'll add a hkt:Placemarks tag with the connection string necessary to grab the data:
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2"
xmlns:gx="http://www.google.com/kml/ext/2.2"
xmlns:hkt="http://code.google.com/p/happy-kml-tool/wiki/Reference">
<Document>
<name>Marine Mammals</name>
<hkt:Placemarks connection="pinnipeds.shp" name="type" />
</Document>
</kml>
Note the connection attribute. This is any connection string that OGR will accept. So if you wanted to connect to a PostGIS database table, it would look something like this: <hkt:Placemarks connection="PG:host=example.marinemap.org user=postgres dbname=marinemap table=pinnipeds" /> Running this through hkt would generate the following output: <?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2"
xmlns:gx="http://www.google.com/kml/ext/2.2"
xmlns:hkt="http://code.google.com/p/happy-kml-tool/wiki/Reference">
<Document>
<name>Marine Mammals</name>
<Placemark>
<name>stlr</name>
<Point>
<coordinates>-122.0822035425683,37.42228990140251,0</coordinates>
</Point>
<ExtendedData>
<Data name="id">
<value>12</value>
</Data>
<Data name="type">
<value>stlr</value>
</Data>
<Data name="year">
<value>1992</value>
</Data>
<Data name="count">
<value>19</value>
</Data>
</ExtendedData>
</Placemark>
<Placemark>...</Placemark>
<Placemark>...</Placemark>
... and on and on
</Document>
</kml>Each feature from the shapefile was converted to kml, bringing along with it all of it's attributes. The name="type" attribute on hkt:Placemarks instructed hkt to use the type attribute to name each placemark. As you can see, this workflow is quite flexible. Placemarks are rendered wherever the hkt:Placemarks tag is, so if we wanted to render this data within a Pinnipeds folder it's easy: <Document>
<name>Marine Mammals</name>
<Folder>
<name>Pinnipeds</name>
<hkt:Placemarks connection="pinnipeds.shp" name="type" />
</Folder>
</Document>You could even do something funky here like add atom:link tags within the Folder, happy-kml-tool won't mind. Splitting Placemarks into Folders by AttributeOften it's desirable to split Placemarks into separate folders based on their attributes. In this case, we'd like to split the pinniped data into folders based on the species. To do that, we'll use the split attribute. <?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2"
xmlns:gx="http://www.google.com/kml/ext/2.2"
xmlns:hkt="http://code.google.com/p/happy-kml-tool/wiki/Reference">
<Document>
<name>Marine Mammals</name>
<Folder>
<name>Pinnipeds</name>
<hkt:Placemarks connection="pinnipeds.shp" name="type" split="type" />
</Folder>
</Document>
</kml>Assuming there are three different species types (stlr, cal, harb), we'll get this output: <?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2"
xmlns:gx="http://www.google.com/kml/ext/2.2"
xmlns:hkt="http://code.google.com/p/happy-kml-tool/wiki/Reference">
<Document>
<name>Marine Mammals</name>
<Folder>
<name>Pinnipeds</name>
<Folder>
<name>stlr</name>
<Placemark>...</Placemark>
<Placemark>...</Placemark>
...
</Folder>
<Folder>
<name>cal</name>
<Placemark>...</Placemark>
<Placemark>...</Placemark>
...
</Folder>
<Folder>
<name>harb</name>
<Placemark>...</Placemark>
<Placemark>...</Placemark>
...
</Folder>
</Folder>
</Document>
</kml>The split attribute accepts multiple, comma separated values. Multiple values will result in nested folders. So if you specified split="type, year" you'd get a folder structure like this:
Setting Attribute AliasesIt's not very desirable to have Folder and Placemark names like "stlr" and "harb". hkt:alias tags can help us here. You must specify an attribute name, a pattern to match (string or regular expression), and the replacement value. Note that these tags go within the hkt:Placemarks tag. Here's an example: <?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2"
xmlns:gx="http://www.google.com/kml/ext/2.2"
xmlns:hkt="http://code.google.com/p/happy-kml-tool/wiki/Reference">
<Document>
<name>Marine Mammals</name>
<Folder>
<name>Pinnipeds</name>
<hkt:Placemarks connection="pinnipeds.shp" name="type" split="type, year">
<hkt:alias attribute="type" match="stlr" value="Stellar Sea Lion" />
<hkt:alias attribute="type" match="cal" value="California Sea Lion" />
<hkt:alias attribute="type" match="harb" value="Harbor Seal" />
<hkt:alias attribute="year" match="\(\d+)\" value="Summer, \1" />
</hkt:Placemarks>
</Folder>
</Document>
</kml>This will convert the attribute values as the appear everywhere within the final document, including the ExtendedData, name tags within placemarks, or anywhere they are used in description tag content. Notice the last hkt:alias uses regular expressions. Within the match attribute, this are denoted with opening and closing backslashes. Within the value attribute, the matched value is referenced via "\1". So "1999" would be converted to "Summer, 1999". The syntax of the regular expression and the contents of value must be acceptable by the re.sub method of the python regular expression module. Now our folder structure looks like this:
Filtering DataIn some cases you will want to filter the data from a particular source so that your only adding some of it to the document. Lets say we have a shapefile with observations of whales, but we want to only include baleen whales. We can use a filter to remove Orcas: <?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2"
xmlns:gx="http://www.google.com/kml/ext/2.2"
xmlns:hkt="http://code.google.com/p/happy-kml-tool/wiki/Reference">
<Document>
<name>Marine Mammals</name>
<Folder>
<name>Pinnipeds</name>
<hkt:Placemarks connection="pinnipeds.shp" name="type" split="type, year">
<hkt:alias attribute="type" match="stlr" value="Stellar Sea Lion" />
<hkt:alias attribute="type" match="cal" value="California Sea Lion" />
<hkt:alias attribute="type" match="harb" value="Harbor Seal" />
<hkt:alias attribute="year" match="\(\d+)\" value="Summer, \1" />
</hkt:Placemarks>
</Folder>
<Folder>
<name>Whales</name>
<hkt:Placemarks connection="whales.shp" name="type" split="type" filter="type != 'orca'">
</Folder>
</Document>
</kml>The filter attribute should contain terms that can be inserted into a WHERE clause using OGR SQL support. See the OGR SQL Documentation for more info. StylingThat's about it as far as adding data to your KML document. All other happy-kml-tool functionality is handled within the hkt:Style tag. Within this tag, you can use a CSS-like syntax to control the style and behavior of your layer. Just like CSS, your style rules are applied to elements in the KML document using selectors and support cascading. CSS was chosen because it's sytax is terse when compared to add XML tags for style but also extremely flexible. <?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2"
xmlns:gx="http://www.google.com/kml/ext/2.2"
xmlns:hkt="http://code.google.com/p/happy-kml-tool/wiki/Reference">
<Document>
<hkt:Style><![CDATA[
Placemark {
icon-scale: 1.5;
}
Placemark[type="Stellar Sea Lion"] {
icon: 'http://marinemap.org/icons/stlr.png';
}
Placemark[type="California Sea Lion"] {
icon: 'http://marinemap.org/icons/cal.png';
}
Placemark[type="Harbor Seal"] {
icon: 'http://marinemap.org/icons/harb.png';
}
Folder[name="Whales"] Placemark {
icon: 'http://marinemap.org/icons/baleen-whale.png';
}
Folder[name="Whales"] Placemark[type='orca'] {
icon: 'http://marinemap.org/icons/killer-whale.png';
}
]]></hkt:Style>
<name>Marine Mammals</name>
<Folder>
<name>Pinnipeds</name>
<hkt:Placemarks connection="pinnipeds.shp" name="type" split="type, year">
<hkt:alias attribute="type" match="stlr" value="Stellar Sea Lion" />
<hkt:alias attribute="type" match="cal" value="California Sea Lion" />
<hkt:alias attribute="type" match="harb" value="Harbor Seal" />
<hkt:alias attribute="year" match="\(\d+)\" value="Summer, \1" />
</hkt:Placemarks>
</Folder>
<Folder>
<name>Whales</name>
<hkt:Placemarks connection="whales.shp" name="type" split="type">
</Folder>
</Document>
</kml>Here you can see how selectors can be used to specify styles both broadly (top-most rule), and specifically (in the case of Placemarks of a certain type. The attribute selectors like Placemark[type="Harbor Seal] are applied to any Placemarks where the attribute type is equal to "Harbor Seal". This matching occurs after any hkt:alias instructions are run. This example also illustrates the cascading nature of these rules. The bottom two rules mean that all Placemarks from the whales.shp dataset will have the baleen-whale.png icon, except for Orcas which will be represented by the killer-whale.png icon. Because of the top rule that applies to all Placemarks, all these icons will be scaled up to 1.5. There are many, many rule types that can be used on different KML features. Check out the Reference wiki for the current list. Special Attribute SelectorsThere are some attribute selectors you can use that don't involve attributes from you data or name tags. Check out the Reference wiki for a full list, but here is an example:
Folder[-feature-count > 2000] {
list-style: checkHideChildren;
}
For Folders that have more than 2000 placemarks, this rule sets the listStyle to checkHideChildren so that the user interface isn't cluttered with a huge list of features. The user won't be able to expand the Folder to see them. This can also be a performance optimization when using a tree widget like kmltree. Other special attribute selectors include:
Pseudo SelectorsOnly one right now, :hover. This selector is used to create StyleMap tags so that the style of a Placemark varies depending on whether the user has it highlighted with their cursor. For example: <?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2"
xmlns:gx="http://www.google.com/kml/ext/2.2"
xmlns:hkt="http://code.google.com/p/happy-kml-tool/wiki/Reference">
<Document>
<hkt:Style><![CDATA[
Folder[name="Whales"] Placemark {
icon: 'http://marinemap.org/icons/baleen-whale.png';
}
Folder[name="Whales"] Placemark[type='orca'] {
icon: 'http://marinemap.org/icons/killer-whale.png';
}
Placemark:hover {
icon: 'http://marinemap.org/icons/whale-highlight.png';
label: @type;
label-color: #fff;
label-opacity: 0.8;
}
]]></hkt:Style>
<name>Marine Mammals</name>
<Folder>
<name>Whales</name>
<hkt:Placemarks connection="whales.shp" name="type" split="type">
</Folder>
</Document>
</kml>In this example all placemarks will change to the whale-highlight.png icon and display the type attribute as a semi-opaque white label when the cursor is placed over them. Splitting into NetworkLinkshappy-kml-tool is designed to create multiple KML/KMZ files that have NetworkLink's pointing to each other as easily as it can create single KML files. The way this is done is by using CSS rules to identify Folders that should be turned into their own documents linked to by a NetworkLink. <?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2"
xmlns:gx="http://www.google.com/kml/ext/2.2"
xmlns:hkt="http://code.google.com/p/happy-kml-tool/wiki/Reference">
<Document>
<hkt:Style><![CDATA[
Folder[name="Whales"] Folder[-num-features > 2000], Folder[name="Whales"] Folder[-file-size > 50 kilobytes] {
networklink: true;
checkHideChildren: true;
}
]]></hkt:Style>
<name>Marine Mammals</name>
<Folder>
<name>Whales</name>
<hkt:Placemarks connection="whales.shp" name="type" split="type">
</Folder>
</Document>
</kml>What this CSS rules does is say if any Folder within the "Whales" Folder than has more than 2000 Placemarks or the rendered size is greater than 50 kilobytes, it should be turned into a NetworkLink. The hkt utility will generate multiple files with relative NetworkLinks so that they can be posted to a webserver and work properly. This makes it easy to use NetworkLinks to increase network and client performance. I've also set the listStyle here to checkHideChildren for an additional performance boost when using the output with kmltree. |