Background: Alright so I am working on an application that uses SharpKML to produce a number of Placemarks that each have properties associated with it. I want to show these properties as custom data elements within the popup balloon.
Problem: I cannot seem to be able to set my balloon style to show all of the extended data elements in a table regardless of how many there are or their name.
Please if you can help with this.
Cheers.
Edit:
Added the KML code that is produced.
<Style id="cim-default-conductors">
<LabelStyle>
<color>ff7814b4</color>
<colorMode>normal</colorMode>
<scale>1</scale>
</LabelStyle>
<LineStyle>
<color>ff7814b4</color>
<colorMode>normal</colorMode>
<width>3</width>
</LineStyle>
<BalloonStyle>
<bgColor>ffffffff</bgColor>
<textColor>ff000000</textColor>
<text><![CDATA[<b>$[name]</b><br/>Conductor Spacing: $[amst:phase_spacing_summary]<br/>Description: $[description]<br/>]]></text>
<displayMode>default</displayMode>
</BalloonStyle>
</Style>
================Different kml doc=======================
<Placemark>
<name>CIM_ACLineSegment Name: 17470033 mRID: _111192568740L1</name>
<description>cim:ACLineSegment</description>
<styleUrl>../master.kml#cim-default-conductors</styleUrl> <--references the produced style that is shown above.
<ExtendedData>
<Data name="mRID">
<value>111192568740L1</value>
</Data>
<Data name="name">
<value>17470033</value>
</Data>
<Data name="length">
<value>30</value>
</Data>
<Data name="ratedVoltage">
<value>600</value>
</Data>
<Data name="conductorMaterial">
<value>other</value>
</Data>
</ExtendedData>
<LineString>
<altitudeMode>relativeToGround</altitudeMode>
<coordinates>126.795933456158,-45.6023587778821,0</coordinates>
</LineString>
</Placemark>
Related
I have a set of events that I want to export to KML. Those events have nested attributes and attributes with cardinality greater than one (maxOccur > 1).
Here is an example of one event presented as JSON:
{
"id": 1,
"date": "2018-01-01",
"client": {
"name": "Brad",
"since": "2017-12-01"
},
"products": [
{
"name": "P1",
"quantity": 20
},
{
"name": "P2",
"quantity": 40
},
]
}
One option that we are considering is use an arbitrary XML at ExtendedData. This option sounds good for Machine-to-Machine data interchange, but not for human read, because the Google Earth does not show those data very well in balloons:
<ExtendedData>
<id>1</id>
<date>2018-01-01</date>
<client>
<name>Brad</name>
<since>2017-12-01</since>
</client>
<products>
<product>
<name>P1</name>
<quantity>20</quantity>
</product>
<product>
<name>P2</name>
<quantity>40</quantity>
</product>
</products>
</ExtendedData>
Other option that we are considering is use Schema + SchemaData + BallonStyle, but the cardinality and nested attributes are not supported...
<Schema name="myEvent" id="myEvent">
<SimpleField type="int" name="id">
<displayName>Event id</displayName>
</SimpleField>
<SimpleField type="string" name="date">
<displayName>Event date</displayName>
</SimpleField>
<!-- workaround to nested data -->
<SimpleField type="string" name="clientName">
<displayName>Client name</displayName>
</SimpleField>
<SimpleField type="string" name="clientSince">
<displayName>Client since</displayName>
</SimpleField>
<!-- no idea how present attributes with more than one occurrence -->
</Schema>
There is another option that we should consider?
Data and SimpleData elements in KML ExtendedData are limited to name/value pairs with values as text but custom XML is allowed using a different namespace.
You can add arbitrary XML data to a Feature in KML from a non-KML XML namespace like Dublin core, RDF, or your own namespace. Here is a related tutorial.
Here's an example with arbitrary Dublin core XML in KML:
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<name>ExtendedData Test with Dublin core XML</name>
<Placemark>
<name>Test data</name>
<ExtendedData xmlns:ns="http://example.org/myapp/">
<ns:metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:description>
KML features a rich mix of
metadata information and services for the XML community.
</dc:description>
<dc:subject>XML, RDF, metadata, information
syndication services</dc:subject>
<dc:identifier>http://earth.google.com</dc:identifier>
<dc:language>en-us</dc:language>
<dc:date>2000-10-13</dc:date>
<dc:rights>Copyright 2000, O'Reilly Network</dc:rights>
<dc:publisher>O'Reilly and Associates, Inc.</dc:publisher>
</ns:metadata>
</ExtendedData>
<Point>
<coordinates>-71.7571,42.953</coordinates>
</Point>
</Placemark>
</Document>
</kml>
With your own namespace, you can store whatever XML you want with whatever nesting.
<Placemark>
<name>Test data</name>
<ExtendedData>
<data xmlns="urn:my.org:data">
<id>1</id>
<date>2018-01-01</date>
<client>
<name>Brad</name>
<since>2017-12-01</since>
</client>
<products>
<product>
<name>P1</name>
<quantity>20</quantity>
</product>
<product>
<name>P2</name>
<quantity>40</quantity>
</product>
</products>
</data>
</ExtendedData>
</Placemark>
Note however that this approach passes along the data within the KML for a machine-to-machine exchange since Google Earth does not process nor display this type of data. A third party application could take advantage of this type of XML data contained within the KML.
If you want at least part of the data to be human-readable via Google Earth, consider a hybrid solution with name-value pairs in Data/value elements for the simple values and a custom XML element for the more complex nested XML data.
<Placemark>
<name>Test data</name>
<ExtendedData>
<Data name="id">
<value>123</value>
</Data>
<Data name="date">
<value>2018-01-01</value>
</Data>
<customData xmlns="urn:custom">
<client>
<name>Brad</name>
<since>2017-12-01</since>
</client>
</customData>
</ExtendedData>
<Point>
<coordinates>-71.7571,42.953</coordinates>
</Point>
</Placemark>
I exported the KML file from locationhistory of google maps.
Thus, I try to create a Fusion Table using Google drive and importing this file.
During the import I selected the row that contains column names.
The columns created in fusion table are:
description, name and geometry
The Fusion Table import only one row with description and name filled, while geometry field is empty.
Are Geography information on Fusion table fused in geometry type ?
If it's right, why it doesn't work?
If it's wrong, what is the correctly type mapping for these information?
Here the KML file used (removed some entries about gx:coord):
<?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:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<Document>
<name>Location history from 09/21/2014 to 09/28/2014</name>
<open>1</open>
<description/>
<StyleMap id="multiTrack">
<Pair>
<key>normal</key>
<styleUrl>#multiTrack_n</styleUrl>
</Pair>
<Pair>
<key>highlight</key>
<styleUrl>#multiTrack_h</styleUrl>
</Pair>
</StyleMap>
<Style id="multiTrack_n">
<IconStyle>
<Icon>
<href>http://earth.google.com/images/kml-icons/track-directional/track-0.png</href>
</Icon>
</IconStyle>
<LineStyle>
<color>99ffac59</color>
<width>6</width>
</LineStyle>
</Style>
<Style id="multiTrack_h">
<IconStyle>
<scale>1.2</scale>
<Icon>
<href>http://earth.google.com/images/kml-icons/track-directional/track-0.png</href>
</Icon>
</IconStyle>
<LineStyle>
<color>99ffac59</color>
<width>8</width>
</LineStyle>
</Style>
<Placemark>
<name>Latitude User</name>
<description>Location history for Latitude User from 09/21/2014 to 09/28/2014</description>
<styleUrl>#multiTrack</styleUrl>
<gx:Track>
<altitudeMode>clampToGround</altitudeMode>
<when>2014-09-21T15:23:46.249-07:00</when>
<gx:coord>99.99999 41.99999 0</gx:coord>
<when>2014-09-21T15:23:47.249-07:00</when>
<gx:coord>99.99999 41.99999 0</gx:coord>
</gx:Track>
</Placemark>
</Document>
</kml>
The supported geometry-types when you import KML are Linestring, Polygon and Point (see: https://support.google.com/fusiontables/answer/174680 )
This would work:
<Placemark>
<name>Latitude User</name>
<description>Location history for Latitude User from 09/21/2014 to 09/28/2014</description>
<Point>
<coordinates>99.99999,41.99999,0</coordinates>
</Point>
</Placemark>
How can I do variable substitution in a kml icon reference? I'm using Google Earth to load the kml, and my image doesn't appear for this simple example:
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<Document>
<name>TestMap</name>
<Style id="Icon1">
<IconStyle>
<Icon>
<href>$[url]</href>
</Icon>
</IconStyle>
</Style>
<Placemark>
<name>Hello World</name>
<styleUrl>#Icon1</styleUrl>
<ExtendedData>
<Data name="url">
<value>http://magiccards.info/scans/en/al/232.jpg</value>
</Data>
</ExtendedData>
<Point>
<coordinates>
0,0,0
</coordinates>
</Point>
</Placemark>
</Document>
</Document>
</kml>
Variable substitution for extended data in KML only works in context of the description so you could show the placemark's data url via the description balloon.
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<name>Data+BalloonStyle</name>
<Style id="balloon-style">
<BalloonStyle>
<text>
<![CDATA[
$[name]<br>
<img src="$[url]"/>
]]>
</text>
</BalloonStyle>
</Style>
<Placemark>
<name>Hello World</name>
<styleUrl>#balloon-style</styleUrl>
<ExtendedData>
<Data name="url">
<value>http://magiccards.info/scans/en/al/232.jpg</value>
</Data>
</ExtendedData>
<Point>
<coordinates>-111.956,33.5043</coordinates>
</Point>
</Placemark>
</Document>
</kml>
See related tutorial for adding custom data which describes using the BalloonStyle Element as a Template
https://developers.google.com/kml/documentation/extendeddata
If you want to display a custom icon via IconStyle per placemark then you need to define an inline Style for each placemark with the appropriate URL.
<Placemark>
<name>Hello World</name>
<Style>
<IconStyle>
<Icon>
<href>http://magiccards.info/scans/en/al/232.jpg</href>
</Icon>
</IconStyle>
</Style>
<Point>
<coordinates>-111.956,33.5043</coordinates>
</Point>
</Placemark>
I've got a question referring to the BalloonStyle Tutorial in the KML documentiation:
BalloonStyle Documentation
Playing around with this KML file:
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<name>ExtendedData+SchemaData</name>
<open>1</open>
<!-- Create a balloon template referring to the user-defined type -->
<Style id="trailhead-balloon-template">
<BalloonStyle>
<text>
<![CDATA[
<h2>My favorite trails!</h2>
<br/><br/>
The $[TrailHeadType/TrailHeadName/displayName] is <i>$[TrailHeadType/TrailHeadName]</i>.
The trail is $[TrailHeadType/TrailLength] miles. <br/>
The climb is $[TrailHeadType/ElevationGain] meters. <br/><br/>
]]>
</text>
</BalloonStyle>
</Style>
<!-- Declare the type "TrailHeadType" with 3 fields -->
<Schema name="TrailHeadType" id="TrailHeadTypeId">
<SimpleField type="string" name="TrailHeadName">
<displayName><![CDATA[<b>Trail Head Name</b>]]></displayName>
</SimpleField>
<SimpleField type="double" name="TrailLength">
<displayName><![CDATA[<i>The length in miles</i>]]></displayName>
</SimpleField>
<SimpleField type="int" name="ElevationGain">
<displayName><![CDATA[<i>change in altitude</i>]]></displayName>
</SimpleField>
</Schema>
<!-- Instantiate some Placemarks extended with TrailHeadType fields -->
<Placemark>
<name>Easy trail</name>
<styleUrl>#trailhead-balloon-template</styleUrl>
<ExtendedData>
<SchemaData schemaUrl="#TrailHeadTypeId">
<SimpleData name="TrailHeadName">Pi in the sky</SimpleData>
<SimpleData name="TrailLength">3.14159</SimpleData>
<SimpleData name="ElevationGain">10</SimpleData>
</SchemaData>
</ExtendedData>
<Point>
<coordinates>-122.000,37.002</coordinates>
</Point>
</Placemark>
<Placemark>
<name>Difficult trail</name>
<styleUrl>#trailhead-balloon-template</styleUrl>
<ExtendedData>
<SchemaData schemaUrl="#TrailHeadTypeId">
<SimpleData name="TrailHeadName">Mount Everest</SimpleData>
<SimpleData name="TrailLength">347.45</SimpleData>
<SimpleData name="ElevationGain">10000</SimpleData>
</SchemaData>
</ExtendedData>
<Point>
<coordinates>-121.998,37.0078</coordinates>
</Point>
</Placemark>
</Document>
</kml>
When you remove one of the SimpleData elements, Google Earth shows the Entity Replacement from the Balloon Template. For example you remove the line <SimpleData name="ElevationGain">10000</SimpleData>
it shows up in the Balloon like "The climb is $[TrailHeadType/ElevationGain] meters." Which doesn't look very nice.
Any idea how to tell Google Earth, that it shouldn't display the line from the balloon, if there is a missing SimpleData?
Thanks in Advance
I don't think* you can do what you are asking here - i.e. apply conditional logic within this style text. I have had a similar requirement in the past (certain pieces of data were missing from some of my placemarks) and I ended up applying different styles for each of the possible scenarios of missing information when generating the KML.
It felt both very clunky and if you have a large number of scenarios of one or more items being missing while others exist it could quickly be a mess.
*Would love to be wrong.
UPDATE
Just looked at my code for this, and I actually ended up putting all the text in the placemark fields of my schema, and printing out empty nodes when I did not have data - also less elegant, but did not require additional styles in the end. Based on your example something like:
<BalloonStyle>
<text>
<![CDATA[
<h2>My favorite trails!</h2>
$[TrailHeadType/TrailHeadName/displayName]
$[TrailHeadType/TrailLengthBalloonText]
$[TrailHeadType/ElevationGainBalloonText]
]]>
</text>
</BalloonStyle>
<Placemark>
<name>Difficult trail</name>
<styleUrl>#trailhead-balloon-template</styleUrl>
<ExtendedData>
<SchemaData schemaUrl="#TrailHeadTypeId">
<SimpleData name="TrailHeadName">Mount Everest</SimpleData>
<SimpleData name="TrailLength">347.45</SimpleData>
<SimpleData name="ElevationGain">10000</SimpleData>
<SimpleData name="TrailHeadNameBalloonText"><![CDATA[The trail name is Mount Everest</br>]]></SimpleData>
<SimpleData name="TrailLengthBalloonText"><![CDATA[The trail is 347.45 miles.</br>]]></SimpleData>
<SimpleData name="ElevationGainBalloonText"><![CDATA[The climb is 10000 meters.</br>]]></SimpleData>
</SchemaData>
</ExtendedData>
<Point>
<coordinates>-121.998,37.0078</coordinates>
</Point>
</Placemark>
Then when you have no data you leave that item blank:
<Placemark>
<name>Difficult trail</name>
<styleUrl>#trailhead-balloon-template</styleUrl>
<ExtendedData>
<SchemaData schemaUrl="#TrailHeadTypeId">
<SimpleData name="TrailHeadName">Mount Everest</SimpleData>
<SimpleData name="TrailLength">347.45</SimpleData>
<SimpleData name="ElevationGain">10000</SimpleData>
<SimpleData name="TrailHeadNameBalloonText"><![CDATA[The trail name is Mount Everest</br>]]></SimpleData>
<SimpleData name="TrailLengthBalloonText"></SimpleData>
<SimpleData name="ElevationGainBalloonText"></SimpleData>
</SchemaData>
</ExtendedData>
<Point>
<coordinates>-121.998,37.0078</coordinates>
</Point>
</Placemark>
As my data are changing somehow quite often, I'm now providing a xml file linked to an css file and embed it as an iframe in the description. For offline use, create a kmz and put the xml and css into it.
For a small tutorial have a look here:
http://www.w3schools.com/xml/xml_display.asp
In the kml, it looks as follows:
<description>
<![CDATA[
<iframe src="http://www.w3schools.com/xml/cd_catalog_with_css.xml"></iframe>
]]>
</description>
Big picture: I'm writing my own KMLs using a custom KML writer in C++. I have a set of placemarks that share some (not all) properties. Currently I store the shared properties as properties of the parent folder the placemarks reside in. These KMLs are viewed in Google Earth.
Users gain access to view this data using a BalloonStyle linked to the ExtendedData stored within each placemark/folder. The placemarks share a balloonstyle for their unique data, and the shared data is displayed using the parent folder's balloon style when they click on the folder in Google Earth.
I can't afford to duplicate the shared data in each placemark, which is why I store it in the parent folder.
Alternative 1: Is there any way to provide a user-clickable link to the parent folder's balloon within the child's balloon?
Alternative 2: Is it possible to display another Placemark/Feature's data in a placemark's info balloon?
As far as I know, both alternatives are impossible.
Edit: Simplified example code:
<?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:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<Document>
<name>MyKml</name>
<Style id="Style8"> <!-- style for child point -->
<BalloonStyle>
<text>
Point: $[name]<br/>
Code: $[code]<br/>
Number of Points: $[numpts] <!-- Alt2: numpts belongs to the parent - this syntax is obviously wrong -->
Link to parent: $[parentid] <!-- Alt1: if I can't display the parent's properties, can I at least give a clickable link to it? -->
</text>
</BalloonStyle>
</Style>
<Style id="Style12"> <!-- style for parent folder -->
<BalloonStyle>
<text>
Point Group: $[name]<br/>
Number of Points: $[numpts]
</text>
</BalloonStyle>
</Style>
<Folder>
<name>Point Group 1</name>
<styleUrl>#Style12</styleUrl>
<ExtendedData>
<Data name="numpts">
<value>4</value>
</Data>
</ExtendedData>
<Placemark>
<name>PT1</name>
<styleUrl>#Style8</styleUrl>
<ExtendedData>
<Data name="code">
<value>TAT1</value>
</Data>
</ExtendedData>
<MultiGeometry>
<Point>
<coordinates>-121,47,110</coordinates>
</Point>
</MultiGeometry>
</Placemark>
<Placemark>
<name>PT2 - PT4</name>
<styleUrl>#Style8</styleUrl>
<ExtendedData>
<Data name="code">
<value>TAT2</value>
</Data>
</ExtendedData>
<MultiGeometry>
<Point>
<coordinates>-121.090,47.430,1224</coordinates>
</Point>
<Point>
<coordinates>-121.470,47.621,122</coordinates>
</Point>
<Point>
<coordinates>-121.990,47.121,122</coordinates>
</Point>
</MultiGeometry>
</Placemark>
</Folder>
</Document>
</kml>
Alternative 1: Is there any way to provide a user-clickable link to the parent folder's balloon within the child's balloon?
This is achieved using feature anchors where you can refer to and link to placemarks by its KML id using <a href="target"> in the description/balloon. If the target Feature has a LookAt or Camera element, the Feature is viewed from the specified viewpoint.
The href can be a fragment URL (that is, a URL with a # sign followed by a KML identifier).
You can also append an action to the URL with a semi-colon (;) and one of these qualifiers:
;flyto (default) - fly to the Feature
;balloon - open the Feature's balloon but do not fly to the Feature
;balloonFlyto - open the Feature's balloon and fly to the Feature
If you want to show the folder balloon from the point's you can update the BalloonStyle text as following and add "id" attribute to the Folder you want to refer to.
<Style id="Style8">
<BalloonStyle>
<text>
<![CDATA[
Point: $[name]<br/>
Code: $[code]<br/>
Number of Points: $[numpts]
<BR>Link to parent
]]>
</text>
</BalloonStyle>
</Style>
<Folder id="parent"> *** Must add "id" attribute to link to it ***
...
</Folder>
Alternative 2: Is it possible to display another Placemark/Feature's data in a placemark's info balloon?
Can't directly include metadata for another placemark in balloon of another but can link to it and switch to showing other's balloon by user clicking the link.
You could add links to the description of the placemarks to link to one another in the same way to link to Folder by its id then add $[description] place holder to the BalloonStyle text.
<Style id="style9"> <!-- style for child point -->
<BalloonStyle>
<text>
<![CDATA[
Point: $[name]<br/>
Code: $[code]<br/>
Link to parent
<br>$[description]
]]>
</text>
</BalloonStyle>
</Style>
...
<Placemark id="pt1">
...
</Placemark>
<Placemark id="pt2">
<name>PT2 - PT4</name>
<description>
<![CDATA[
Show P1<BR>
]]>
</description>
<styleUrl>#style9</styleUrl>
...
</Placemark>