0

I need to make an average of latitude and longitude. Please find below the XML :

<gml:posList>-52.02545860348812 -173.671875 -52.02545860348812 -173.583984375 -52.18740474559968 -173.583984375 -52.18740474559968 -173.671875 -52.02545860348812 -173.671875</gml:posList>

Knowing that my XML is formed in the following way and the number of iteration/points is random :

<gml:posList>lat1 long1 lat2 long2 lat3 long3 etc...</gml:posList>

in output, i want something like that :

<centerOf>-52.1064317 -173.62793</centerOf>

My level in XSLT is really bad, i hope to find some help here.

Thank you, Martin

Martin L.
  • 5
  • 4
  • Which XSLT 1.0 processor will you be using? You could really use some extension functions here, if your processor supports them. Start by looking into **tokenizing** the list into pairs of lat/lon values. – michael.hor257k Nov 20 '18 at 14:49
  • i cannot use any extension functions unfortunately. I can just use basic function from XSLT 1.0. – Martin L. Nov 20 '18 at 14:54
  • How do you know that? Which processor is it? – michael.hor257k Nov 20 '18 at 14:56
  • My dev tell me : JAXP XSLT APIs (javax.xml.transform) to transform xml file. With XSL Transform, the engine is Saxon 6.5.5 (i have the same result) – Martin L. Nov 21 '18 at 08:29
  • I believe Java's native processor is Xalan (you can verify this as explained here: https://stackoverflow.com/questions/25244370/how-can-i-check-which-xslt-processor-is-being-used-in-solr/25245033#25245033). Xalan supports the EXSLT `str:tokenize()` extension function, Saxon 6.5 does not. Without such support, you will need to do it yourself see: https://stackoverflow.com/a/31593480/3016153 – michael.hor257k Nov 21 '18 at 08:44
  • P.S. If you can use Saxon 6.5, how come you cannot use the current version? – michael.hor257k Nov 21 '18 at 08:45
  • I can confirm it's Xalan, my dev give me this link in order to help me : https://xml.apache.org/xalan-j/trax.html And with your link to verify ==> 1.0 - Apache Software Foundation (Xalan XSLTC) I will take a look with tokenizing – Martin L. Nov 21 '18 at 12:26
  • To be honest, my level with XSLT is bad. I don't understand how to use tokenizing with my exemple :( – Martin L. Nov 22 '18 at 09:03

2 Answers2

1

This gets

 <centerOf>-52.09023706033274 -173.63671875</centerOf>

from the input

<x xmlns:gml="whatever">
 <gml:posList>-52.02545860348812 -173.671875 -52.02545860348812 -173.583984375 -52.18740474559968 -173.583984375 -52.18740474559968 -173.671875 -52.02545860348812 -173.671875</gml:posList>
</x>

and stylesheet

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
        xmlns:gml="whatever"
        exclude-result-prefixes="gml">

 <xsl:template match="gml:posList">
  <centerOf>
   <xsl:call-template name="a"/>
  </centerOf>
 </xsl:template>

 <xsl:template name="a">
  <xsl:param name="n" select="0"/>
  <xsl:param name="lat" select="0"/>
  <xsl:param name="long" select="0"/>
  <xsl:param name="s" select="normalize-space(.)"/>
  <xsl:choose>
   <xsl:when test="string-length($s)=0">
    <xsl:value-of select="$lat div $n"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="$long div $n"/>
   </xsl:when>
   <xsl:otherwise>
    <xsl:variable name="s2" select="concat(substring-after($s,' '), ' ')"/>
    <xsl:call-template name="a">
     <xsl:with-param name="n" select="$n+1"/>
     <xsl:with-param name="lat" select="$lat + substring-before($s,' ')"/>
     <xsl:with-param name="long" select="$long + substring-before($s2,' ')"/>
     <xsl:with-param name="s" select="normalize-space(substring-after($s2,' '))"/>
    </xsl:call-template>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

at each iteration you strip off two numbers and accumulate the two running totals, then recurse until the string of remaining values is empty.

David Carlisle
  • 5,582
  • 1
  • 19
  • 23
0

For the sake of completeness, when using a processor that supports the EXSLT str:tokenize() function (such as Xalan), this could be done shortly and sweetly as:

XSLT 1.0 (+ EXSLT str:tokenize())

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:str="http://exslt.org/strings"
xmlns:gml="http://www.opengis.net/gml/3.2"
exclude-result-prefixes="str gml">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/">
    <xsl:variable name="tokens" select="str:tokenize(gml:posList, ' ')" />
    <xsl:variable name="count-points" select="count($tokens) div 2" />
    <centerOf>
        <xsl:value-of select="sum($tokens[position() mod 2 = 1]) div $count-points"/>
        <xsl:text> </xsl:text>
        <xsl:value-of select="sum($tokens[position() mod 2 = 0]) div $count-points"/>
    </centerOf>
</xsl:template>

</xsl:stylesheet>

Demo: http://xsltransform.hikmatu.com/3NzcBsG


Note that averaging the given coordinates is not necessarily the same thing as calculating the center point: http://www.geomidpoint.com/methods.html

michael.hor257k
  • 113,275
  • 6
  • 33
  • 51