What I usually do is read it as XML, then feed the entire xml_fragment into a GeometryReplacer (set to GML as geometry type). Once that has built the geometry you should be able to use a GeometryExtractor to get it back to WKT.
I tried that with your sample and noticed that in WKT the arcs are replaced with linestrings though, so instead of building a 7-part geometry it's a single line. I'm pretty sure that's not what you want. I seem to recall a recent support case where FME handled different kinds of GML arcs in different ways, wonder if that's popping up here too... @TiaAtSafe helped me with that one (C133450)
The easiest is probably to first convert the GML to a regular FME geometry, then convert to WKT using the GeometryExtractor.
The GeometryReplacer works fine if you only send the <Curve> tags, you can use an XMLQueryExtractor with the following XQuery expression:
/*/*
Then send the result to the GeometryReplacer set to "GML". You may want to consider using the ArcStroker before converting to WKT.
Thanks, for the lines this works well but, as you said, the arc's do not work correctly
If arc is first this will be the result:
<?xml version="1.0" encoding="UTF-16"?><imgeo:kruinlijnOndersteunendWegdeel xmlns:imgeo="http://www.geostandaarden.nl/imgeo/2.1/stuf-imgeo/1.3">
<Curve srsName="urn:ogc:def:crs:EPSG::28992" xmlns="http://www.opengis.net/gml">
<segments>
<Arc>
<posList srsDimension="2">189254.476 431090.159 189254.986 431092.715 189255.406 431095.286</posList>
</Arc>
</segments>
</Curve>
</imgeo:kruinlijnOndersteunendWegdeel>
Is transformed into:
POINTÂ (189181.75710323136Â 431105.99751930835)
instead of :
CIRCULARSTRING(189254.476Â 431090.159,189254.986Â 431092.715,189255.406Â 431095.286)

Same if line is first and then the curve. Cruves just dont work like i would expect
For a mix starting with a line and than a curve i would expect somthing like this in WKT:
COMPOUNDCURVE((160888.904
445450.594,160888.999Â 445450.71,160888.334Â 445451.253,160882.813
445455.057),CIRCULARSTRING(160882.813Â 445455.057,160881.86Â 445455.715,160880.916
445456.387))

The easiest is probably to first convert the GML to a regular FME geometry, then convert to WKT using the GeometryExtractor.
The GeometryReplacer works fine if you only send the <Curve> tags, you can use an XMLQueryExtractor with the following XQuery expression:
/*/*
Then send the result to the GeometryReplacer set to "GML". You may want to consider using the ArcStroker before converting to WKT.
Â
Thanks, this got things going at least. but the extractor does not give the right results, or atleast not i would expect.
Hi @JeroenR, FME doesn't seem to support converting an Arc geometry to the OGC WKT 'CIRCULARSTRING' representation. If it's allowed to transform arcs to approximate line strings, you can use the ArcStroker, as @david_r mentioned before.
Otherwise, I think you will have to implement the conversion from GML to OGC WKT.
My ultimate weapon - XQuery: If the input GML fragment is always "Curve" consisting of "Arc" or "LineStringSegment", the XMLXQueryExtractor with this XQuery expression directly converts the GML to OGC WKT.
declare default element namespace "http://www.opengis.net/gml";
let $t := {
    for $segment in //Curve/segments/*
    let $coords := fn:string-join({
        let $c := fn:tokenize($segment/posList/text(), ' ')
        for $i in (1 to fn:count($c)) where $i mod 2 eq 1
        return $cm$i]||' '||$ct$i + 1]
    }, ',')
    return if (fn:name($segment) eq 'Arc')
        then 'CIRCULARSTRING('||$coords||')'
        else '('||$coords||')'
}
return 'COMPOUNDCURVE('||fn:string-join($t, ',')||')'

Hi @JeroenR, FME doesn't seem to support converting an Arc geometry to the OGC WKT 'CIRCULARSTRING' representation. If it's allowed to transform arcs to approximate line strings, you can use the ArcStroker, as @david_r mentioned before.
Otherwise, I think you will have to implement the conversion from GML to OGC WKT.
My ultimate weapon - XQuery: If the input GML fragment is always "Curve" consisting of "Arc" or "LineStringSegment", the XMLXQueryExtractor with this XQuery expression directly converts the GML to OGC WKT.
declare default element namespace "http://www.opengis.net/gml";
let $t := {
    for $segment in //Curve/segments/*
    let $coords := fn:string-join({
        let $c := fn:tokenize($segment/posList/text(), ' ')
        for $i in (1 to fn:count($c)) where $i mod 2 eq 1
        return $cm$i]||' '||$ct$i + 1]
    }, ',')
    return if (fn:name($segment) eq 'Arc')
        then 'CIRCULARSTRING('||$coords||')'
        else '('||$coords||')'
}
return 'COMPOUNDCURVE('||fn:string-join($t, ',')||')'

Nice! That's some black-belt XQuery!
Hi @JeroenR, FME doesn't seem to support converting an Arc geometry to the OGC WKT 'CIRCULARSTRING' representation. If it's allowed to transform arcs to approximate line strings, you can use the ArcStroker, as @david_r mentioned before.
Otherwise, I think you will have to implement the conversion from GML to OGC WKT.
My ultimate weapon - XQuery: If the input GML fragment is always "Curve" consisting of "Arc" or "LineStringSegment", the XMLXQueryExtractor with this XQuery expression directly converts the GML to OGC WKT.
declare default element namespace "http://www.opengis.net/gml";
let $t := {
    for $segment in //Curve/segments/*
    let $coords := fn:string-join({
        let $c := fn:tokenize($segment/posList/text(), ' ')
        for $i in (1 to fn:count($c)) where $i mod 2 eq 1
        return $cm$i]||' '||$ct$i + 1]
    }, ',')
    return if (fn:name($segment) eq 'Arc')
        then 'CIRCULARSTRING('||$coords||')'
        else '('||$coords||')'
}
return 'COMPOUNDCURVE('||fn:string-join($t, ',')||')'

Â
This looks like the solution.I did not get it working for my single LineStringSegment gml but will try to edit it so it will work if there is only a single LineStringSegment in the GML.My working day is over so will keep you posted here :) Thanks!
Hi @JeroenR, FME doesn't seem to support converting an Arc geometry to the OGC WKT 'CIRCULARSTRING' representation. If it's allowed to transform arcs to approximate line strings, you can use the ArcStroker, as @david_r mentioned before.
Otherwise, I think you will have to implement the conversion from GML to OGC WKT.
My ultimate weapon - XQuery: If the input GML fragment is always "Curve" consisting of "Arc" or "LineStringSegment", the XMLXQueryExtractor with this XQuery expression directly converts the GML to OGC WKT.
declare default element namespace "http://www.opengis.net/gml";
let $t := {
    for $segment in //Curve/segments/*
    let $coords := fn:string-join({
        let $c := fn:tokenize($segment/posList/text(), ' ')
        for $i in (1 to fn:count($c)) where $i mod 2 eq 1
        return $cm$i]||' '||$ct$i + 1]
    }, ',')
    return if (fn:name($segment) eq 'Arc')
        then 'CIRCULARSTRING('||$coords||')'
        else '('||$coords||')'
}
return 'COMPOUNDCURVE('||fn:string-join($t, ',')||')'

Not really a star in xQuery.
Â
Â
Is it possible to add something like:Â
Â
Â
declare default element namespace "http://www.opengis.net/gml";
let $t := {
for $segment in //Curve/segments/*
let $coords := fn:string-join({
let $c := fn:tokenize($segment/posList/text(), ' ')
for $i in (1 to fn:count($c)) where $i mod 2 eq 1
return $c/$i]||' '||$cn$i + 1]
If count(total_amount_of_objects_in_segments) = 1:
 if (fn:name($segment) eq 'Arc' or 'gml:Arc')
   then return 'CIRCULARSTRING('||$coords||')'
elif (fn:name($segment) eq 'LineStringSegment' or 'gml:LineStringSegment')
then return 'LINESTRING('||$coords||')'
else error
else  if count(total_amount_of_objects_in_segments) > 1:
Your compound script that makes compound curves.
Edit: The code elow does not work.
geotest.fmw
It looks like this works, but is it really correct?
Somehow theÂ
    else if (fn:name($segment) != 'LineStringSegment')        then 'LINESTRING('||$coords||')'        else '('||$coords||')'
does not feel right but it gives the correct results in my test workflow.
declare default element namespace "http://www.opengis.net/gml";let $t := {    for $segment in //Curve/segments/*    let $coords := fn:string-join({        let $c := fn:tokenize($segment/posList/text(), ' ')        for $i in (1 to fn:count($c)) where $i mod 2 eq 1        return $ct$i]||' '||$c $i + 1]    }, ',')    return if (fn:name($segment) eq 'Arc')        then 'CIRCULARSTRING('||$coords||')'    else if (fn:name($segment) != 'LineStringSegment')        then 'LINESTRING('||$coords||')'        else '('||$coords||')'}return if (fn:starts-with(fn:string-join($t, ','), 'LINESTRING') eq true)     then $t    else 'COMPOUNDCURVE('||fn:string-join($t, ',')||')'
Hi @JeroenR, FME doesn't seem to support converting an Arc geometry to the OGC WKT 'CIRCULARSTRING' representation. If it's allowed to transform arcs to approximate line strings, you can use the ArcStroker, as @david_r mentioned before.
Otherwise, I think you will have to implement the conversion from GML to OGC WKT.
My ultimate weapon - XQuery: If the input GML fragment is always "Curve" consisting of "Arc" or "LineStringSegment", the XMLXQueryExtractor with this XQuery expression directly converts the GML to OGC WKT.
declare default element namespace "http://www.opengis.net/gml";
let $t := {
    for $segment in //Curve/segments/*
    let $coords := fn:string-join({
        let $c := fn:tokenize($segment/posList/text(), ' ')
        for $i in (1 to fn:count($c)) where $i mod 2 eq 1
        return $cm$i]||' '||$ct$i + 1]
    }, ',')
    return if (fn:name($segment) eq 'Arc')
        then 'CIRCULARSTRING('||$coords||')'
        else '('||$coords||')'
}
return 'COMPOUNDCURVE('||fn:string-join($t, ',')||')'

Need to clarify the conditions and requirements.
Â
Conditions:
Â
- The GML geometry representation is always a "Curve" element which contains a single "segments" element.
- The "segments" element can contain one or more "LineStringSegment" or "Arc" element.
Requirements: I'm unclear the 4th requirement below.
Â
- If the "segments" consists of just a single "Arc" element, it should be converted to "CIRCULARSTRING".
- If the "segments" consists of just a single "LineStringSegment" element, it should be converted to "LINESTRING".
- If the "segments" consists of two or more children and one or more of them is "Arc", it should be converted to "COMPOUNDCURVE" consisting of some "LINESTRING" and some "CIRCULARSTRING".
- If the "segment" consists of two or more children and all of them are "LineStringSegment", it should be converted to ?
Edit: The code elow does not work.
geotest.fmw
It looks like this works, but is it really correct?
Somehow theÂ
    else if (fn:name($segment) != 'LineStringSegment')        then 'LINESTRING('||$coords||')'        else '('||$coords||')'
does not feel right but it gives the correct results in my test workflow.
declare default element namespace "http://www.opengis.net/gml";let $t := {    for $segment in //Curve/segments/*    let $coords := fn:string-join({        let $c := fn:tokenize($segment/posList/text(), ' ')        for $i in (1 to fn:count($c)) where $i mod 2 eq 1        return $ct$i]||' '||$c $i + 1]    }, ',')    return if (fn:name($segment) eq 'Arc')        then 'CIRCULARSTRING('||$coords||')'    else if (fn:name($segment) != 'LineStringSegment')        then 'LINESTRING('||$coords||')'        else '('||$coords||')'}return if (fn:starts-with(fn:string-join($t, ','), 'LINESTRING') eq true)     then $t    else 'COMPOUNDCURVE('||fn:string-join($t, ',')||')'
:( it only works because the variable segment is named gml:LineStringSegment and LineStringSegment. If both are the same the above does nog work at all.Â
Â
Â
Need to clarify the conditions and requirements.
Â
Conditions:
Â
- The GML geometry representation is always a "Curve" element which contains a single "segments" element.
- The "segments" element can contain one or more "LineStringSegment" or "Arc" element.
Requirements: I'm unclear the 4th requirement below.
Â
- If the "segments" consists of just a single "Arc" element, it should be converted to "CIRCULARSTRING".
- If the "segments" consists of just a single "LineStringSegment" element, it should be converted to "LINESTRING".
- If the "segments" consists of two or more children and one or more of them is "Arc", it should be converted to "COMPOUNDCURVE" consisting of some "LINESTRING" and some "CIRCULARSTRING".
- If the "segment" consists of two or more children and all of them are "LineStringSegment", it should be converted to ?
I updated the dummycode above:
Â
Â
Conditions:
Â
Â
- Yes GML geometry representation is always a "Curve" element with a single Segment
- The "segments" element can contain one or more "LineStringSegment" or "Arc" element but cannot contain more than one LineStringSegments if there are no arc's.
- If the "segments" consists of just a single "LineStringSegment" element it should be a "LINESTRING".Multiple LineStingSegments without any Arc's should not be in the data.
- Arc should always be converted to "CIRCULARSTRING" but if there is only 1 Arc it should not become a COMPOUNDCURVE .
- Yes, the "segments" who consists of two or more children and one or more of them is "Arc", it should be converted to "COMPOUNDCURVE" consisting of lines (withou LINESTRING: '('||$coords||')') and some "CIRCULARSTRING".
Hi @JeroenR, FME doesn't seem to support converting an Arc geometry to the OGC WKT 'CIRCULARSTRING' representation. If it's allowed to transform arcs to approximate line strings, you can use the ArcStroker, as @david_r mentioned before.
Otherwise, I think you will have to implement the conversion from GML to OGC WKT.
My ultimate weapon - XQuery: If the input GML fragment is always "Curve" consisting of "Arc" or "LineStringSegment", the XMLXQueryExtractor with this XQuery expression directly converts the GML to OGC WKT.
declare default element namespace "http://www.opengis.net/gml";
let $t := {
    for $segment in //Curve/segments/*
    let $coords := fn:string-join({
        let $c := fn:tokenize($segment/posList/text(), ' ')
        for $i in (1 to fn:count($c)) where $i mod 2 eq 1
        return $cm$i]||' '||$ct$i + 1]
    }, ',')
    return if (fn:name($segment) eq 'Arc')
        then 'CIRCULARSTRING('||$coords||')'
        else '('||$coords||')'
}
return 'COMPOUNDCURVE('||fn:string-join($t, ',')||')'

This expression may be close to your requirement. This expression returns empty string when the input didn't match any condition. The "fn:local-name" function returns the node name except namespace qualifier.
Â
declare default element namespace "http://www.opengis.net/gml";
declare function local:coords($p as element(posList)) as xs:string
{
    fn:string-join({
        let $c := fn:tokenize($p/text(), '\s+')
        for $i in (1 to fn:count($c)) where $i mod 2 eq 1
        return $co$i]||' '||$ce$i + 1]Â
    }, ',')
};
let $segments := //Curve/segments/*
return if (fn:count($segments) eq 1)
    then {
        if (fn:local-name($segmentso1]) eq 'Arc')
            then 'CIRCULARSTRING('|| local:coords($segments|1]/posList) ||')'
        else if (fn:local-name($segmentse1]) eq 'LineStringSegment')
            then 'LINESTRING('|| local:coords($segmentsÂ1]/posList) ||')'
        else ()
    }
    else if (1 < fn:count($segments))
    then {
        let $parts := {
            for $s in $segments
            let $coords := local:coords($s/posList)
            return if (fn:local-name($s) eq 'Arc')
                then 'CIRCULARSTRING('|| $coords ||')'
                else '('|| $coords ||')'
        }
        return 'COMPOUNDCURVE('|| fn:string-join($parts, ',') ||')'
    }
    else ()
Â
This expression may be close to your requirement. This expression returns empty string when the input didn't match any condition. The "fn:local-name" function returns the node name except namespace qualifier.
Â
declare default element namespace "http://www.opengis.net/gml";
declare function local:coords($p as element(posList)) as xs:string
{
    fn:string-join({
        let $c := fn:tokenize($p/text(), '\s+')
        for $i in (1 to fn:count($c)) where $i mod 2 eq 1
        return $c $i]||' '||$cf$i + 1]Â
    }, ',')
};
let $segments := //Curve/segments/*
return if (fn:count($segments) eq 1)
    then {
        if (fn:local-name($segments 1]) eq 'Arc')
            then 'CIRCULARSTRING('|| local:coords($segments 1]/posList) ||')'
        else if (fn:local-name($segments 1]) eq 'LineStringSegment')
            then 'LINESTRING('|| local:coords($segments/1]/posList) ||')'
        else ()
    }
    else if (1 < fn:count($segments))
    then {
        let $parts := {
            for $s in $segments
            let $coords := local:coords($s/posList)
            return if (fn:local-name($s) eq 'Arc')
                then 'CIRCULARSTRING('|| $coords ||')'
                else '('|| $coords ||')'
        }
        return 'COMPOUNDCURVE('|| fn:string-join($parts, ',') ||')'
    }
    else ()
Â
@takashiÂ
Â
Â
I have a question about your code and how to convert it to accept polygons? Using your script it generates a line from the outer line that can be transformed to a polygon by just adding CURVEPOLYGON() to the string. Would it be possible to loop the part below "let $segments := //Curve/segments/*" with the same same option as like in the curves?
Â
Â
This is an example for an polygon that only got the exterior layer:Â
Â
Â
CURVEPOLYGON(COMPOUNDCURVE((144734.339Â 478871.234,144726.116Â 478866.683,144721.989Â 478874.296,144730.113Â 478878.77,144734.371Â 478881.114,144734.303Â 478881.189,144734.164Â 478881.289),CIRCULARSTRING(144734.164Â 478881.289,144731.421Â 478882.294,144728.561Â 478882.886),CIRCULARSTRING(144728.561Â 478882.886,144727.633Â 478883.047,144726.698Â 478883.158),CIRCULARSTRING(144726.698Â 478883.158,144725.069Â 478883.233,144723.44Â 478883.159),(144723.44Â 478883.159,144721.185Â 478882.943,144714.355Â 478879.602,144682.01Â 478863.689),CIRCULARSTRING(144682.01Â 478863.689,144680.652Â 478862.715,144679.681Â 478861.354),CIRCULARSTRING(144679.681Â 478861.354,144678.672Â 478858.547,144678.584Â 478855.565),CIRCULARSTRING(144678.584Â 478855.565,144679.407Â 478852.683,144680.715Â 478849.987),(144680.715Â 478849.987,144681.996Â 478848.051,144684.391Â 478845.04,144686.158Â 478842.817,144687.119Â 478841.754,144688.152Â 478840.611,144690.832Â 478837.647,144702.259Â 478824.291,144703.152Â 478823.248,144707.042Â 478818.762),CIRCULARSTRING(144707.042Â 478818.762,144709.288Â 478816.375,144711.726Â 478814.184),(144711.726Â 478814.184,144717.306Â 478809.181,144719.539Â 478810.783,144719.833Â 478810.039,144723.115Â 478811.253,144723.233Â 478810.934,144732.692Â 478814.492,144744.69Â 478819.005,144747.047Â 478812.905,144743.754Â 478811.663,144743.578Â 478812.131,144742.614Â 478811.769,144742.93Â 478810.927,144735.259Â 478808.049,144725.687Â 478804.457,144724.014Â 478808.571,144719.732Â 478807.033,144719.974Â 478806.805,144720.358Â 478806.488),CIRCULARSTRING(144720.358Â 478806.488,144725.31Â 478803.273,144730.74Â 478800.957),CIRCULARSTRING(144730.74Â 478800.957,144735.978Â 478800.133,144741.267Â 478800.513),(144741.267Â 478800.513,144743.047Â 478801.18,144748.647Â 478804.123,144749.028Â 478804.365,144751.113Â 478806.607,144751.409Â 478806.906,144751.377Â 478807.029,144756.296Â 478812.026,144756.98Â 478812.785,144762.456Â 478818.126,144771.628Â 478827.332,144775.854Â 478831.56,144775.483Â 478835.895,144768.396Â 478843.718,144764.279Â 478848.203,144760.712Â 478852.28,144751.004Â 478862.771,144742.884Â 478871.68,144740.438Â 478870.348,144738.654Â 478873.622,144734.339Â 478871.234)))
Â
@takashiÂ
Â
Â
I have a question about your code and how to convert it to accept polygons? Using your script it generates a line from the outer line that can be transformed to a polygon by just adding CURVEPOLYGON() to the string. Would it be possible to loop the part below "let $segments := //Curve/segments/*" with the same same option as like in the curves?
Â
Â
This is an example for an polygon that only got the exterior layer:Â
Â
Â
CURVEPOLYGON(COMPOUNDCURVE((144734.339Â 478871.234,144726.116Â 478866.683,144721.989Â 478874.296,144730.113Â 478878.77,144734.371Â 478881.114,144734.303Â 478881.189,144734.164Â 478881.289),CIRCULARSTRING(144734.164Â 478881.289,144731.421Â 478882.294,144728.561Â 478882.886),CIRCULARSTRING(144728.561Â 478882.886,144727.633Â 478883.047,144726.698Â 478883.158),CIRCULARSTRING(144726.698Â 478883.158,144725.069Â 478883.233,144723.44Â 478883.159),(144723.44Â 478883.159,144721.185Â 478882.943,144714.355Â 478879.602,144682.01Â 478863.689),CIRCULARSTRING(144682.01Â 478863.689,144680.652Â 478862.715,144679.681Â 478861.354),CIRCULARSTRING(144679.681Â 478861.354,144678.672Â 478858.547,144678.584Â 478855.565),CIRCULARSTRING(144678.584Â 478855.565,144679.407Â 478852.683,144680.715Â 478849.987),(144680.715Â 478849.987,144681.996Â 478848.051,144684.391Â 478845.04,144686.158Â 478842.817,144687.119Â 478841.754,144688.152Â 478840.611,144690.832Â 478837.647,144702.259Â 478824.291,144703.152Â 478823.248,144707.042Â 478818.762),CIRCULARSTRING(144707.042Â 478818.762,144709.288Â 478816.375,144711.726Â 478814.184),(144711.726Â 478814.184,144717.306Â 478809.181,144719.539Â 478810.783,144719.833Â 478810.039,144723.115Â 478811.253,144723.233Â 478810.934,144732.692Â 478814.492,144744.69Â 478819.005,144747.047Â 478812.905,144743.754Â 478811.663,144743.578Â 478812.131,144742.614Â 478811.769,144742.93Â 478810.927,144735.259Â 478808.049,144725.687Â 478804.457,144724.014Â 478808.571,144719.732Â 478807.033,144719.974Â 478806.805,144720.358Â 478806.488),CIRCULARSTRING(144720.358Â 478806.488,144725.31Â 478803.273,144730.74Â 478800.957),CIRCULARSTRING(144730.74Â 478800.957,144735.978Â 478800.133,144741.267Â 478800.513),(144741.267Â 478800.513,144743.047Â 478801.18,144748.647Â 478804.123,144749.028Â 478804.365,144751.113Â 478806.607,144751.409Â 478806.906,144751.377Â 478807.029,144756.296Â 478812.026,144756.98Â 478812.785,144762.456Â 478818.126,144771.628Â 478827.332,144775.854Â 478831.56,144775.483Â 478835.895,144768.396Â 478843.718,144764.279Â 478848.203,144760.712Â 478852.28,144751.004Â 478862.771,144742.884Â 478871.68,144740.438Â 478870.348,144738.654Â 478873.622,144734.339Â 478871.234)))
Â
And the source of the aboveÂ
Â
gml-met-curves-en-interior-geo-zonder-cruves.txtÂ
Hi @JeroenR, FME doesn't seem to support converting an Arc geometry to the OGC WKT 'CIRCULARSTRING' representation. If it's allowed to transform arcs to approximate line strings, you can use the ArcStroker, as @david_r mentioned before.
Otherwise, I think you will have to implement the conversion from GML to OGC WKT.
My ultimate weapon - XQuery: If the input GML fragment is always "Curve" consisting of "Arc" or "LineStringSegment", the XMLXQueryExtractor with this XQuery expression directly converts the GML to OGC WKT.
declare default element namespace "http://www.opengis.net/gml";
let $t := {
    for $segment in //Curve/segments/*
    let $coords := fn:string-join({
        let $c := fn:tokenize($segment/posList/text(), ' ')
        for $i in (1 to fn:count($c)) where $i mod 2 eq 1
        return $cm$i]||' '||$ct$i + 1]
    }, ',')
    return if (fn:name($segment) eq 'Arc')
        then 'CIRCULARSTRING('||$coords||')'
        else '('||$coords||')'
}
return 'COMPOUNDCURVE('||fn:string-join($t, ',')||')'

Reuse the previous code maximal. Hope this works fine.
Â
declare default element namespace "http://www.opengis.net/gml/";
declare function local:coords($p as element(posList)) as xs:string
{
    fn:string-join({
        let $c := fn:tokenize($p/text(), '\s+')
        for $i in (1 to fn:count($c)) where $i mod 2 eq 1
        return $cp$i]||' '||$cÂ$i + 1]Â
    }, ',')
};
declare function local:ring-to-ogc-wkt-curve($r as element(Ring)) as xs:string
{
    let $segments := $r//Curve/segments/*
    return if (fn:count($segments) eq 1)
        then {
            if (fn:local-name($segments<1]) eq 'Arc')
                then 'CIRCULARSTRING('|| local:coords($segments:1]/posList) ||')'
            else if (fn:local-name($segments<1]) eq 'LineStringSegment')
                then 'LINESTRING('|| local:coords($segmentsÂ1]/posList) ||')'
            else ()
        }
        else if (1 < fn:count($segments))
        then {
            let $parts := {
                for $s in $segments
                let $coords := local:coords($s/posList)
                return if (fn:local-name($s) eq 'Arc')
                    then 'CIRCULARSTRING('|| $coords ||')'
                    else '('|| $coords ||')'
            }
            return 'COMPOUNDCURVE('|| fn:string-join($parts, ',') ||')'
        }
        else ()   Â
};
for $p in //PolygonPatch
return 'CURVEPOLYGON(' || fn:string-join({
    for $r in $p/*oself::exterior or self::interior]/*
    return
        if (fn:local-name($r) eq 'Ring')
            then local:ring-to-ogc-wkt-curve($r) Â
        else if (fn:local-name($r) eq 'LinearRing')Â
            then 'LINESTRING('|| local:coords($r/posList) ||')'
        else ()
}, ',') || ')'