I'd like to display a kmlfiles with just points on a HERE map as a heatmap.
I've seen the heatmap example and KML examples but could someone help me combine the two so I can make a heatmap from my KML file?
Transform your KML data to an array of nokia.maps.heatmap.Overlay.DataPoint elements. The data set can then be used as in the Heat Map Example
The precise transformation will depend on where in the KML your data is held (and whether you need to pass in values as well, but the following XSLT should help get you started:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
data = [
<xsl:for-each select="kml/Document/Folder/Placemark/Point">
{
<xsl:variable name="coordinates">
<xsl:value-of select="*" />
</xsl:variable>
<xsl:variable name="longitude">
<xsl:value-of select="substring-before($coordinates,',')"/>
</xsl:variable>
<xsl:variable name="latitude">
<xsl:value-of select="substring-before(substring-after($coordinates,','),',')"/>
</xsl:variable>
latitude: <xsl:value-of select="$latitude" />,
longitude: <xsl:value-of select="$longitude" />
}
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:for-each>
];
</xsl:template>
</xsl:stylesheet>
Alter kml/Document/Folder/Placemark/Point as necessary if your points aren't in a folder for example.
Related
I would like to build map based on csv file.
Map declaration:
<xsl:variable name="myMap" as="map(xs:string, array(xs:string))">
Csv file:
key1;value1
key1;value2
key2;value3
So map should be composed of two elements:
key1 => array ['value1', 'value2']
key2 => array ['value3']
I've tried to create map like:
<xsl:variable name="myMap" as="map(xs:string, array(xs:string))">
<xsl:map>
<xsl:if test="unparsed-text-available($csv-file, $csv-encoding)">
<xsl:variable name="csv" select="unparsed-text($csv-file, $csv-encoding)"/>
<xsl:analyze-string select="$csv" regex="\r\n?|\n">
<xsl:non-matching-substring>
<xsl:variable name="row" select="tokenize(., '\t')"/>
<xsl:variable name="key" select="$row[1]"/>
<xsl:variable name="array_element" select="$row[2]"/>
<xsl:map-entry key="$key" select="$array_element"/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:if>
</xsl:map>
</xsl:variable>
but I couldn't find a way to merge map entries.
My second approach was to firstly declare map:
<xsl:variable name="myMap" as="map(xs:string, array(xs:string))">
<xsl:map/>
</xsl:variable>
and then I tried to fill it based on csv file content like this:
<xsl:if test="unparsed-text-available($csv-file, $csv-encoding)">
<xsl:variable name="csv" select="unparsed-text($csv-file, $csv-encoding)" />
<xsl:analyze-string select="$csv" regex="\r\n?|\n">
<xsl:non-matching-substring>
<xsl:variable name="row" select="tokenize(., '\t')"/>
<xsl:variable name="key" as="xs:string" select="$row[1]"/>
<xsl:variable name="value" as="xs:string" select="$row[2]"/>
<xsl:choose>
<xsl:when test="map:contains($myMap, $key)">
<xsl:variable name="valueArray" select="map:get($myMap,$key)"/>
<xsl:sequence select="array:append($valueArray, $value)" />
<xsl:sequence select="map:put($myMap, $key, $valueArray) />
</xsl:when>
<xsl:otherwise>
<xsl:variable name="valueArray" as="array(xs:string)" select="[]"/>
<xsl:sequence select="array:append($valueArray, $value)" />
<xsl:sequence select="map:put($myMap, $key, $valueArray) />
</xsl:otherwise>
</xsl:choose>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:if>
Is it possible to invoke array:append and map:put methods from template?
I think, given that you use XSLT 3, you can treat that as a grouping problem where you group the lines from your CSV on the substring-before(., ';'):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="csv-string" as="xs:string">key1;value1
key1;value2
key2;value3</xsl:param>
<xsl:variable name="myMap" as="map(xs:string, array(xs:string))">
<xsl:map>
<xsl:for-each-group select="tokenize($csv-string, '\r?\n')[normalize-space()]" group-by="substring-before(., ';')">
<xsl:map-entry key="current-grouping-key()" select="array{ current-group()!tokenize(substring-after(., ';'), ';') }"/>
</xsl:for-each-group>
</xsl:map>
</xsl:variable>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="json" indent="yes" />
<xsl:template match="/" name="xsl:initial-template">
<xsl:sequence select="$myMap"/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/nc4NzRb
For the example I have inlined the contents from your CSV but you could as well use <xsl:for-each-group select="unparsed-text-lines('file.csv')" group-by="substring-before(., ';')"> instead to load from a file.
As for building and merging maps, if you don't need an array(xs:string) as the map value but can live with a sequence of strings you could use the map:merge function with an option to combine values of the same key:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="csv-string" as="xs:string">key1;value1
key1;value2
key2;value3</xsl:param>
<xsl:variable name="myMap" as="map(xs:string, xs:string*)"
select="map:merge(
tokenize($csv-string, '\r?\n')[normalize-space()]
!
(
let $values := tokenize(., ';')
return map { head($values) : tail($values) }
),
map { 'duplicates' : 'combine' }
)"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="adaptive" indent="yes" />
<xsl:template match="/" name="xsl:initial-template">
<xsl:sequence select="$myMap"/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/nc4NzRb/1
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:html="http://www.w3.org/TR/REC-html40"
xmlns:pi="urn:com.workday/picof">
I have the following map:
<xsl:variable name="tabsNames">
<entry><name>A</name><value>one</value></entry>
<entry><name>B</name><value>two</value></entry>
</xsl:variable>
I would like to iterate the map to get each key and value assigned to a variable:
<xsl:for-each select="$tabsNames/element()">
<xsl:variable name="tabName" select="./entry/name"/>
<xsl:variable name="tabValue" select="./entry/value"/>
</xsl:for-each>
How should the select look like in order to get name and value?
Well, a variable alone does nothing but given your variable with a temporary tree containing some entry elements if you want to process them with for-each and have the name and value in your variables inside of the for-each use e.g.
<xsl:for-each select="$tabsNames/entry">
<xsl:variable name="tabName" select="name"/>
<xsl:variable name="tabValue" select="value"/>
</xsl:for-each>
Given the namespaces you have shown in your edit of the question you have two choices, either you need to make sure your temporary elements in the variable do not end up in the default namespace xmlns="urn:schemas-microsoft-com:office:spreadsheet" you have on your stylesheet, you can do that with
<xsl:variable name="tabsNames" xmlns="">
<entry><name>A</name><value>one</value></entry>
<entry><name>B</name><value>two</value></entry>
</xsl:variable>
then my suggestion above remains valid, or you need to adjust your paths with e.g.
<xsl:for-each select="$tabsNames/ss:entry">
<xsl:variable name="tabName" select="ss:name"/>
<xsl:variable name="tabValue" select="ss:value"/>
</xsl:for-each>
or
<xsl:for-each select="$tabsNames/entry" xpath-default-namespace="urn:schemas-microsoft-com:office:spreadsheet">
<xsl:variable name="tabName" select="name"/>
<xsl:variable name="tabValue" select="value"/>
</xsl:for-each>
Source XML
<?xml version="1.0" encoding="UTF-8"?>
<root>
<parent1>
<child1>P1-Child-01</child1>
<child2>P1-Child-02</child2>
</parent1>
<parent2>
<child3>P2-Child-03</child3>
<child4>P2-Child-04</child4>
<child5>P2-Child-05</child5>
</parent2>
<parent3>
<child6>P2-Child-03</child6>
<child7>P2-Child-04</child7>
</parent3>
</root>
Required output
<root>
<parent1>
<cld1>P1-Child-01</cld1>
<cld2>P1-Child-02</cld2>
</parent1>
<parent2>
<cld3>P2-Child-03</cld3>
<cld4>P2-Child-04</cld4>
</parent2>
</root>
i tried creating an XSLT with below logic:
1. It will get the number of parent elements
2. based on that for each parent element i want to get the number of child elements and based on string concat(chld,$index) i want to create new elements
i.e
replacing
<child1>P1-Child-01</child1>
with
<cld4>P2-Child-04</cld4>
but i stuck no how to get the count of child elements for each parent
current Xslt
<xsl:template match="/">
<xsl:call-template name="parentforloop"></xsl:call-template>
</xsl:template>
<xsl:template name="parentforloop" >
<xsl:param name="index" select ="1" />
<xsl:param name="total" select="count(/*/*)"/>
<xsl:param name="parentName" select="concat('parent',$index)"/>
<xsl:element name="{concat('parent',$index )}">
</xsl:element>
<xsl:if test="not($index=$total)">
<xsl:call-template name="parentforloop">
<xsl:with-param name="index" select="$index+1"></xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
Please help
thank You
I have this XSLT to split a 25 MB XHTML file.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="html/body"/>
</xsl:template>
<xsl:template match="body">
<xsl:for-each-group select="node()"
group-starting-with="*[position()=1 or #class='toc']">
<xsl:if test="count(current-group()[self::*]) > 0 ">
<xsl:variable name="filename" select="concat('/home/t',position(),'.xml' )"/>
<xsl:apply-templates/>
<xsl:result-document
indent="yes" method="xml" href="$filename}">
<html>
<xsl:copy-of select="/html/#*"/>
<xsl:for-each select="/html/node()">
<xsl:choose>
<xsl:when test="not(self::body)">
<xsl:copy-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:copy-of select="current-group()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</html>
</xsl:result-document>
</xsl:if>
</xsl:for-each-group>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
It currently works at splitting up the file when it finds a #toc. I need to alter this to be sensitive to size of the output file, as opposed to breaking at the #toc.
Desired end state: I want the result document to be about 500KB. I suppose position() might be the best way to regulate the split points?? I tried various string-length() approaches--I could not get one to work. Also, I think white space may be an issue.
By my calculations with these documents, splitting the file at a <p class="i0"> found at or near every 150th position increment should reliably give me the filesize I need.
I guess the best way to get there is to change this:
group-starting-with="*[position()=1 or #class='toc']"
So far I have not succeeded in anything I have changed it to. Thoughts?
UPDATE: I'm not ready to say this is answered, because someone may have a better idea. But right now I'm using group-starting-with="body/*[position()=1 or position() mod 350 = 0]" with some success. It is testing well.
UPDATE 2: The group-starting-with="body/*[position()=1 or position() mod 350 = 0]" is not working well. Problem is that it is the position within the for-each-loop, not the overall file.
The successful solution ended up being an xslt 3.0 accumulator.
As an alternative:
Dmitiri Novatchev solution for XSLT 1.0:
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="vResult">
<xsl:apply-templates/>
</xsl:variable>
Length of output is: <xsl:text/>
<xsl:value-of select="concat(string-length($vResult), '
')"/>
<xsl:if test="string-length($vResult) <= 1800">
<xsl:copy-of select="$vResult"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on this source.xml:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
produces the wanted result:
Length of output is: 51
01
02
03
04
05
06
07
08
09
10
References
XSLT FAQ: WML and HDML - Measuring the size of the output file, in bytes
XSLT 3.0: Accumulator Function
Utilizing new capabilities of XML languages to verify integrity constraints
A Functional Tokenizer (Was: Re: Looping over a CSV in XSL)
XSL Techniques
FXSL:sumTree
I have a dateTime variable, and I want to convert it to a decimal value of epoch.
How can this be done?
I tried using:
seconds-from-duration($time, xs:dateTime('1970-01-01T00:00:00'))
but it just returns 0.
Please advice.
Thanks.
This transformation:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:sequence select="current-dateTime()"/>
<xsl:sequence select=
"( current-dateTime() - xs:dateTime('1970-01-01T00:00:00') )
div
xs:dayTimeDuration('PT1S')
"/>
</xsl:template>
</xsl:stylesheet>
when applied on any XML document (not used), produces the wanted result -- the current date-time and its Unix epoch (the number of seconds since 1/1/1970 ):
2010-08-12T06:26:54.273-07:00 1281594414.273
A pure xsl 1.0 lib example:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:date="https://github.com/ilyakharlamov/pure-xsl/date"
version="1.0">
<xsl:import href="https://raw.github.com/ilyakharlamov/pure-xsl/master/date.xsl"/>
<xsl:template match="/">
<xsl:variable name="time_as_timestamp" select="1365599995640"/>
<xsl:text>time_as_timestamp:</xsl:text><xsl:value-of select="$time_as_timestamp"/><xsl:text>
</xsl:text>
<xsl:variable name="time_as_xsdatetime">
<xsl:call-template name="date:date-time">
<xsl:with-param name="timestamp" select="$time_as_timestamp"/>
</xsl:call-template>
</xsl:variable>
<xsl:text>time_as_xsdatetime:</xsl:text><xsl:value-of select="$time_as_xsdatetime"/><xsl:text>
</xsl:text>
<xsl:text>converted back:</xsl:text>
<xsl:call-template name="date:timestamp">
<xsl:with-param name="date-time" select="$time_as_xsdatetime"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
Output:
time_as_timestamp:1365599995640
time_as_xsdatetime:2013-04-10T13:19:55.640Z
converted back:1365599995640
As an xpath which does not use division but extracts from the duration:
for $i in (current-dateTime()-xs:dateTime('1970-01-01T00:00:00Z'))
return ((days-from-duration($i)*86400)+(hours-from-duration($i)*3600)+(minutes-from-duration($i)*60)+(seconds-from-duration($i)))