Wrox Programmer Forums
Go Back   Wrox Programmer Forums > XML > XSLT
|
XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
 
Old April 19th, 2009, 11:50 PM
Authorized User
 
Join Date: Mar 2009
Posts: 50
Thanks: 7
Thanked 0 Times in 0 Posts
Default tokenize function use required - please help

Hello all. This is now my third, and definitely final request for help as my project is now only this step away from being completed.

It's been a while since I dealt with this particular XSLT file as I have been programming the rest of the site and other translations (like for KML files etc).

Now my problem has come about because the format for holding position coordinates (eastings, northings, altitudes etc) has changed with one of the XML applications I make use of, diggs.

Basically before the data was held like this (making use of GML):
Code:
<gml:Point>
   <gml:pos>316865 504940</gml:pos>
</gml:Point>
And I then used a different element to hold the altitude value.

I used the following function to separate those two coordinates:
Code:
  <xsl:function name="geo:point" as="xs:integer+">
    <xsl:param name="location" as="element(geo:location)"/>
    <xsl:variable name="point" as="xs:string" select="$location/gml:Point[@srsName='EPSG:27700']/gml:pos"/>
    <xsl:sequence select="xs:integer(substring-before($point, ' '))"/>
    <xsl:sequence select="xs:integer(substring-after($point, ' '))"/>
  </xsl:function>
The variable inside of the template would then be something like:
Code:
<xsl:variable name="crest" as="xs:integer+" select="geo:point(geo:slope/geo:crest/geo:location)"/>
This could then be accessed with code like $crest[1] and $crest[2].

Now I am having to deal with the following XML:
Code:
<gml:LineString srsName="urn:ogc:def:crs:epsg:6.9:27700" srsDimension="3" gml:id="cl_BH11">
   <gml:posList>316865 504940 749 407725 268614 -70</gml:posList>
</gml:LineString>
The attribute 'srsDimension' means that each coordinate has three dimensions, in this case (due to the srsName specified) it is an Easting, Northing and Altitude. So here there are two coordinates specified.

What I need to be able to do is break those values down so I can hold each value in a separate variable. So I have been trying to figure out how to use the tokenize function but I am failing miserably and figured it's probably very simple if you know how, so here I am asking for help! I tried to use something like the following code (to just deal with three values, not size) but it didnt work:
Code:
<xsl:function name="geo:point" as="xs:integer+">

   <xsl:param name="location" as="element(geo:location)" />

   <xsl:variable name="point" as="xs:string" select="$location/gml:Point/gml:pos" />
   
    <xsl:sequence select="tokenize($point, ' ')" />
</xsl:function>
Saxon gives me an error with this of xsl:variable needing a '>' or a '/>' which makes no sense as it has '/>' on the end! But that's the sort of thing I'm looking for but for 6 values, and then hopefully I can use the function as I did with the old one.


Once I've sorted this then I have probably a couple of hours work max left to do on my project and then I get it up online and share with you. Will be interesting to hear what you think.

Many thanks (5am - not good!)

Last edited by jamesdurham; April 19th, 2009 at 11:55 PM.. Reason: typo fixing
 
Old April 20th, 2009, 05:49 AM
Friend of Wrox
 
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
Default

As for the error you get, you will need to post a minimal but complete XML input and XSLT stylesheet allowing is to reproduce the problem.
The only thing I see as wrong is that you say you have XML like this
Code:
<gml:LineString srsName="urn:ogc:def:crs:epsg:6.9:27700" srsDimension="3" gml:id="cl_BH11">
   <gml:posList>316865 504940 749 407725 268614 -70</gml:posList>
</gml:LineString>
where your description suggests you want to tokenize the contents of the 'gml:posList' element but then you have
Code:
<xsl:variable name="point" as="xs:string" select="$location/gml:Point/gml:pos" />
in your function where you try to access a different element 'gml:pos' instead.

So that does not make sense to me, but it is a logical error, not a syntax error you seem to encounter.
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
 
Old April 20th, 2009, 05:56 AM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

>Saxon gives me an error with this of xsl:variable needing a '>' or a '/>' which makes no sense

If you read the error message more carefully you will see that Saxon is merely passing on a message from the XML parser - don't blame the messenger! Your stylesheet is not well-formed XML - I can't tell from the fragments you have posted why that should be so, (it's probably due to misuse of quotation marks), but if you use a decent XML editor to edit the source then it will probably help you to avoid this kind of trivial mistake.
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
 
Old April 20th, 2009, 07:24 AM
Authorized User
 
Join Date: Mar 2009
Posts: 50
Thanks: 7
Thanked 0 Times in 0 Posts
Default

Thanks for your replies.

@mhkay - sorry, I was not blaming saxon of course! There was obviously an error somewhere in there, but it wasn't trivial for me to see and I was working in oXygen.

That error doesn't matter though - it was an example of the type of thing I was working on but I'm sure it wasn't going anywhere as I've not well understood things I've read on using tokenize...not enough atleast to do what I need to.

@Martin. I think I must have typed something strangely in my original post because the variable you quoted was for use with the substring-before function I had been using and the XML you quoted was the new XML which needs a new function.

Okay so this is a more complete fragment of the XML I'm working with:
Code:
<geo:slopeData>
   <geo:case>
         <geo:slope>
              <Diggs xmlns="http://schemas.diggsml.com/1.0a">
                    <projects>
                        <Project>
                            <gml:name>DIGGS Example files</gml:name>
                            <id>CASE-101</id>
                            <timing>
                                <startDateTime>2006-03-01T00:00:00</startDateTime>
                            </timing>
                            <locations>
                                <Hole xmlns="http://schemas.diggsml.com/1.0a/geotechnical">
                                    <diggs:primaryName>BH1</diggs:primaryName>
                                    <diggs:geometry>
                                        <gml:MultiCurve>
                                            <gml:curveMember>
                                                <gml:LineString srsName="urn:ogc:def:crs:epsg:6.9:27700" srsDimension="3" gml:id="cl_BH11">
                                                    <gml:posList>316865 504940 749 407725 268614 -70</gml:posList>
                                                </gml:LineString>
                                            </gml:curveMember>
                                        </gml:MultiCurve>
                                    </diggs:geometry>
                                   <diggs:id>CASE-101B1</diggs:id>
                                    <diggs:top>
                                        <gml:pos>0</gml:pos>
                                    </diggs:top>
                                    <layerSystems>
                                        <LayerSystem>
                                            <type codeSpace="DIGGS_LayerSystems">Engineering Geology</type>
                                            <layers>
                                                <Layer>
                                                    <diggs:primaryName>1</diggs:primaryName>
                                                    <diggs:id>LAYER-1</diggs:id>
                                                    <top>
                                                        <gml:pos>0</gml:pos>
                                                    </top>
                                                    <base>
                                                        <gml:pos>50</gml:pos>
                                                    </base>
                                                    <classifications>
                                                        <Classification>
                                                            <system codeSpace="DIGGS_Classification">AGS Legend
                                                                Codes</system>
                                                            <code codeSpace="AGS_LegendCodes">102</code>
                                                        </Classification>
                                                    </classifications>
                                                    <descriptions>
                                                        <Description>
                                                            <system codeSpace="DIGGS_DescriptiveSystem">BS1377
                                                                Engineering Geology</system>
                                                            <value>made ground</value>
                                                        </Description>
                                                    </descriptions>
                                                </Layer>
                                                ..many Layer elements similar to above..and then..
                                      </layers>
                                 </....and so on
(Note that there is much more XML content between some of the higher tags, but that aren't important here.)

I would like the tokenize function to work in a similar fashion to how the substring-before/after function worked, in that I can place a function outside of the template and then use that function to split apart of the values and hold the in 6different variables within the template.

Here is where I would like to use the function in order to create variables holding singular coordinate values
Code:
    <!-- Ground layer variables -->
    <xsl:variable name="groundLayers" as="element(layer)*">
      <xsl:for-each-group
        select="geo:slope/geo:groundLayers/Diggs/projects/Project/locations/Hole/layerSystems/LayerSystem/layers/Layer"
        group-by="diggs:primaryName">
        <xsl:sort select="current-grouping-key()"/>
        <layer xmlns="">
          <xsl:variable name="layerName" select="diggs:primaryName"/>
          <xsl:variable name="layerId" select="diggs:id" as="xs:string"/>
          <xsl:variable name="layerDescription"
            select="descriptions/Description/value"/>
          <layerId>
            <xsl:value-of select="$layerId"/>
          </layerId>
          <layerDescription>
            <xsl:value-of select="$layerDescription"/>
          </layerDescription>
          <layerName>
            <xsl:value-of select="$layerName"/>
          </layerName>

          <xsl:for-each select="current-group()">
            <layer-data>
              <xsl:variable name="layerTopDepth" select="top/gml:pos"/>
              <xsl:variable name="layerBaseDepth" select="base/gml:pos"/>
              <xsl:variable name="groundLayer" as="xs:integer+"
                select="parent::layers/parent::LayerSystem/parent::layerSystems/parent::Hole/geo:posList(.)"/>
              <xsl:variable name="coordinateX1" as="xs:integer" select="$groundLayer[1]"/>
              <xsl:variable name="coordinateY1" as="xs:integer" select="$groundLayer[2]"/>
              <xsl:variable name="coordinateZ2" as="xs:integer" select="$groundLayer[3]"/>
              <xsl:variable name="coordinateX2" as="xs:integer" select="$groundLayer[4]"/>
              <xsl:variable name="coordinateY2" as="xs:integer" select="$groundLayer[5]"/>
              <xsl:variable name="coordinateZ2" as="xs:integer" select="$groundLayer[6]"/>
 
              <coordinateX1>
                <xsl:value-of select="$coordinateX1"/>
              </coordinateX1>
              <coordinateY1>
                <xsl:value-of select="$coordinateY1"/>
              </coordinateY1>
              <coordinateZ1>
                <xsl:value-of select="$coordinateZ1"/>
              </coordinateZ1>
              <coordinateX2>
                <xsl:value-of select="$coordinateX2"/>
              </coordinateX2>
              <coordinateY2>
                <xsl:value-of select="$coordinateY2"/>
              </coordinateY2>
              <coordinateZ2>
                <xsl:value-of select="$coordinateZ2"/>
              </coordinateZ2>
              <layerTopDepth>
                <xsl:value-of select="$layerTopDepth"/>
              </layerTopDepth>
              <layerBaseDepth>
                <xsl:value-of select="$layerBaseDepth"/>
              </layerBaseDepth>
            </layer-data>
          </xsl:for-each>
        </layer>
      </xsl:for-each-group>
    </xsl:variable>
Note that there will be more variables in there but they have no effect on the requirements for this.
I have named the function geo:posList here so the name ties in with the gml:posList where it extracts it's coordinates from.

These values would then be used lower down by concatenating the variables as required..but again, this information isn't needed here and I would be tricky to compose correctly without the exact tokenize function in place ready.

So my questions is, would it please be possible to help me to code a tokenize function that will split the coordinates in the gml:posList element in to those six groundLayer variables shown above so that I can use the values separately.

edit: I don't know if this fragment of slightly guessed code will help at all, but I envisage the tokenize function to look kind of like:
Code:
  <xsl:function name="geo:posList" as="xs:integer+">
    
   <xsl:param name="geometry" as="element(diggs:geometry)" />

       <xsl:variable name="posList" as="xs:string"
 select="$geometry/gml:MultiCurve/gml:curveMember/gml:LineString[@srsName='urn:ogc:def:crs:epsg:6.9:27700]'/gml:posList" />

       <xsl:sequence select="tokenize($posList, ' ')" />

  </xsl:function>
PLease please excuse that this will not be correct, but I hope it can highlight the type of thing I am looking for if possible. Obviously the xs:sequence would have to work for 6 values and not two as above, but I didn't know how to accommodate for that!

Thank you

Last edited by jamesdurham; April 20th, 2009 at 09:07 AM.. Reason: replaced a word
 
Old April 20th, 2009, 09:19 AM
Friend of Wrox
 
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
Default

Here is an example of an XSLT 2.0 stylesheet with a function tokenizing a string to sequence of xs:integer values:
Code:
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:geo="http://example.com/geo"
  xmlns:gml="http://example.com/gml"
  exclude-result-prefixes="geo xs gml"
  version="2.0">
  
  <xsl:function name="geo:point" as="xs:integer+">
    <xsl:param name="posList" as="element(gml:posList)" />
    <xsl:sequence select="for $p in tokenize($posList, ' ') return xs:integer($p)" />
  </xsl:function>
  
  <xsl:template match="/">
    <!-- example call of the function -->
    <xsl:value-of select="geo:point(gml:posList)"/>
  </xsl:template>

</xsl:stylesheet>
You would then need to incorporate such a function in your stylesheet and fix the namespaces to the ones your XML has respectively you want to use, then you can call it on that gml:posList element you have in your input data.
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
The Following User Says Thank You to Martin Honnen For This Useful Post:
jamesdurham (April 20th, 2009)
 
Old April 20th, 2009, 11:56 AM
Authorized User
 
Join Date: Mar 2009
Posts: 50
Thanks: 7
Thanked 0 Times in 0 Posts
Default

hi Martin

that's bloomin' brilliant! Thanks ever so much. I have created a small test xml and xsl and after about an hour or so I have got it to do everything I needed it to, so now I'll plug it in to the main xsl file, shouldn't take too long.

I'll post back to let you know when it's all done. I need some fresh air first!

edit: ok it's all sorted now! was fairly tricky because it had a widespread effect in that and another xsl file. Here's the use of the function you posted Martin (though I renamed the function):
Code:
    <!-- Ground layer variables -->
    <xsl:variable name="groundLayers" as="element(layer)*">
      <!-- geo:slope/geo:groundLayers/diggs:Diggs in front of 'select' -->
      <xsl:for-each-group
        select="geo:slope/geo:groundLayers/diggs:Diggs/diggs:projects/diggs:Project/diggs:locations/diggs_geo:Hole/diggs_geo:layerSystems/diggs_geo:LayerSystem/diggs_geo:layers/diggs_geo:Layer"
        group-by="diggs:id/substring(., 7, 2)" > <!-- selects the 7nd and 8th values in the id -->
        <xsl:sort select="current-grouping-key()"/>
        <layer xmlns="">
          <xsl:variable name="layerName" select="diggs:primaryName"/>
          <xsl:variable name="layerId" select="diggs:id" as="xs:string"/>
          <xsl:variable name="layerDescription" select="diggs_geo:descriptions/diggs_geo:Description/diggs_geo:value"/>
          <xsl:variable name="layerClassification" select="diggs_geo:classifications/diggs_geo:Classification/diggs_geo:code"/>
          
          <layerId>
            <xsl:value-of select="$layerId"/>
          </layerId>
          <layerDescription>
            <xsl:value-of select="$layerDescription"/>
          </layerDescription>
          <layerName>
            <xsl:value-of select="$layerName"/>
          </layerName>
          
          <xsl:for-each select="current-group()">
            <layer-data>
              <xsl:variable name="layerTopDepth" select="diggs_geo:top/gml:pos"/>
              <xsl:variable name="layerBaseDepth" select="diggs_geo:base/gml:pos"/>
              <xsl:variable name="groundLayer" as="xs:integer+"
                select="parent::diggs_geo:layers/parent::diggs_geo:LayerSystem/parent::diggs_geo:layerSystems/preceding-sibling::diggs:geometry/child::gml:MultiCurve/child::gml:curveMember/child::gml:LineString/geo:posList(gml:posList)"/>
              <!-- x y and z stand for easting northing and altitude respecively -->
              <!-- 1 stands for first coordinates, these represent the top of the hole -->
              <xsl:variable name="coordinateX1" as="xs:integer" select="$groundLayer[1]"/>
              <xsl:variable name="coordinateY1" as="xs:integer" select="$groundLayer[2]"/>
              <xsl:variable name="coordinateZ1" as="xs:integer" select="$groundLayer[3]"/>
              <!-- 2 stands for the second coordinates, these represent the bottom of the hole -->
              <xsl:variable name="coordinateX2" as="xs:integer" select="$groundLayer[4]"/>
              <xsl:variable name="coordinateY2" as="xs:integer" select="$groundLayer[5]"/>
              <xsl:variable name="coordinateZ2" as="xs:integer" select="$groundLayer[6]"/>
              
              <xsl:variable name="c2" as="xs:decimal" select="$groundLayer[2] - (($m2)*$groundLayer[1])"/>
              <xsl:variable name="cdiff" as="xs:decimal" select="$c1 - $c2"/>
              <xsl:variable name="xi" as="xs:decimal" select="$cdiff div $mdiff"/>
              <xsl:variable name="yi" as="xs:decimal" select="($m2*$xi)+$c2"/>
              <xsl:variable name="xdiff" as="xs:decimal" select="$toe[1] - $xi"/>
              <xsl:variable name="ydiff" as="xs:decimal" select="$toe[2] - $yi"/>
              
              <xsl:variable name="distanceGL" as="xs:decimal" select="xs:integer(round(math:sqrt($ydiff * $ydiff + $xdiff * $xdiff)))" />
              <!-- Names of height and altitude are strictly the wrong way round! -->   
              <xsl:variable name="heightGL" as="xs:integer" select="$groundLayer[3]"/><!-- [3] is the altitude coord of top of hole -->
              <xsl:variable name="altitudeGL" as="xs:integer" select="$heightGL - $altitudeToe" />
              
              <layerTopDepth>
                <xsl:value-of select="$layerTopDepth"/>
              </layerTopDepth>
              <layerBaseDepth>
                <xsl:value-of select="$layerBaseDepth"/>
              </layerBaseDepth>
              <coordinateX1>
                <xsl:value-of select="$coordinateX1"/>
              </coordinateX1>
              <coordinateY1>
                <xsl:value-of select="$coordinateY1"/>
              </coordinateY1>
              <heightGL>
                <xsl:value-of select="$heightGL"/>
              </heightGL>
              <distanceGL>
                <xsl:value-of select="$distanceGL"/>
              </distanceGL>
              <altitudeGL>
                <xsl:value-of select="$altitudeGL"/>
              </altitudeGL>
              <!-- not used -->
              <coordinateX2>
                <xsl:value-of select="$coordinateX2"/>
              </coordinateX2>
              <coordinateY2>
                <xsl:value-of select="$coordinateY2"/>
              </coordinateY2>
              <coordinateZ2>
                <xsl:value-of select="$coordinateZ2"/>
              </coordinateZ2>
              <!-- same as heightGL -->
              <coordinateZ1>
                <xsl:value-of select="$coordinateZ1"/>
              </coordinateZ1>
            </layer-data>
          </xsl:for-each>
        </layer>
      </xsl:for-each-group>
    </xsl:variable>
Had to change grouping types etc as well. The SVG path drawing part stayed pretty much the same as it was before.

Thanks again. Will post back when things are online

Last edited by jamesdurham; April 20th, 2009 at 08:31 PM..





Similar Threads
Thread Thread Starter Forum Replies Last Post
tokenize bbvic XSLT 1 July 19th, 2007 04:10 PM
<xsl:for-each select="tokenize($indoc,'&#xA;')"> kapy_kal XSLT 4 June 9th, 2006 07:33 AM
tokenize sakura C# 1 December 3rd, 2005 10:43 AM
Functions replace and tokenize not found. spencer.clark XSLT 2 July 20th, 2005 02:51 PM
retreive function/Line from macro or function? MikoMax J2EE 0 April 1st, 2004 04:42 AM





Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.