-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[GEOS-11368] Allow Freemarker templates to update MapML responses #368
[GEOS-11368] Allow Freemarker templates to update MapML responses #368
Conversation
2fbc91a
to
7f5007e
Compare
7190732
to
32c7542
Compare
<map-geometry> | ||
<map-point> | ||
<map-coordinates><#list gattribute.rawValue.coordinates as coord> | ||
<#if coord?index == 0><![CDATA[<span class="desired">]]>${coord.x} ${coord.y}<![CDATA[</span>]]><#else>${coord.x} ${coord.y}</#if></#list></map-coordinates></map-point> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be <map-span>. Note that the <span> you've inserted seems to be escaped in the output - maybe you don't wan't the CDATA sections?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Freemarker is rejecting the tags inside of the if logic unless it was escaped. I'll check to see if there is a better workaround.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be . Note that the you've inserted seems to be escaped in the output - maybe you don't wan't the CDATA sections?
Ok, fixed. The switch to list seems to have corrected the issue where CDATA escaping was required.
|
||
<mapml- xmlns="http://www.w3.org/1999/xhtml"><map-head><map-title>Bridges</map-title><map-meta charset="UTF-8"/><map-meta content="text/mapml" http-equiv="Content-Type"/><map-meta name="extent" content="top-left-longitude=-180.000000,top-left-latitude=90.000000,bottom-right-longitude=180.000000,bottom-right-latitude=-90.000000"/><map-meta name="cs" content="gcrs"/><map-meta name="projection" content="MapML:urn:ogc:def:crs:EPSG::4326"/><map-style>.bbox {display:none} .Bridges-r1-s1{r:48.0; stroke-opacity:1.0; stroke-dashoffset:0; well-known-name:square; stroke-width:1.0; opacity:1.0; fill:#808080; fill-opacity:1.0; stroke:#000000; stroke-linecap:butt}</map-style></map-head><map-body><map-feature id="Bridges.1107531599613" class="Bridges-r1-s1"><map-geometry><map-point><map-coordinates><span class="desired">0.0007 0.0002</span></map-coordinates></map-point></map-geometry><map-properties><table xmlns="http://www.w3.org/1999/xhtml"><thead><tr><th role="columnheader" scope="col">Property name</th><th role="columnheader" scope="col">Property value</th></tr></thead><tbody><tr><th scope="row">FID</th><td itemprop="FID">110</td></tr><tr><th scope="row">NAME</th><td itemprop="NAME">Cam Bridge</td></tr></tbody></table></map-properties></map-feature></map-body></mapml-> | ||
|
||
Note that in addition to tagging the coordinates with a style class, the template also changes the name of the NAME property to "UPDATED NAME" and the value to "CHANGED Cam Bridge". |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok but the output still has <th scope="row">NAME</th><td itemprop="NAME">Cam Bridge</td></tr>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
<#list 0 ..< attribute.rawValue.getNumGeometries() as index> | ||
<#assign polygon = attribute.rawValue.getGeometryN(index)> | ||
<map-polygon> | ||
<#assign shell = polygon.getExteriorRing()><map-coordinates><#list shell.coordinates as coord><#if coord?index == 0><![CDATA[<span class="desired">]]>${coord.x} ${coord.y}<#elseif coord?index == 4> ${coord.x} ${coord.y}<![CDATA[</span>]]><#else> ${coord.x} ${coord.y}</#if></#list></map-coordinates> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be <map-span>, or feel free to use <map-a href="example.org" target="_top">${coord.x} ${coord.y}</map-a> if you like
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
<map-polygon> | ||
<#assign shell = polygon.getExteriorRing()><map-coordinates><#list shell.coordinates as coord><#if coord?index == 0><![CDATA[<span class="desired">]]>${coord.x} ${coord.y}<#elseif coord?index == 4> ${coord.x} ${coord.y}<![CDATA[</span>]]><#else> ${coord.x} ${coord.y}</#if></#list></map-coordinates> | ||
<#list 0 ..< polygon.getNumInteriorRing() as index> | ||
<#assign hole = polygon.getInteriorRingN(index)><map-coordinates><#list hole.coordinates as coord><#if coord?index == 0><![CDATA[<span class="desired">]]>${coord.x} ${coord.y} <#elseif coord?index == 4> ${coord.x} ${coord.y}<![CDATA[</span>]]><#else> ${coord.x} ${coord.y}</#if></#list></map-coordinates></#list> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<map-span>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
Thanks for your hard work on this Joe. I tried to run this branch this morning, but geoserver wouldn't start. I'll try again from home tonight. |
@prushforth Any further luck with testing? |
I am getting bean creation errors when starting geoserver that prevent it from starting:
will play around a bit more |
Had an application deployed with a different spring version I think. Has started up now, will test it a bit. |
@turingtestfail sorry bit of confusion on my part, but I was only able to start the server when I had no data in the geoserver data dir. When I try to start with the data from the release profile, I get the spring GML preview bean error above, geoserver won't start. |
OK after more messing about I got the server to start. I had to |
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
The viewer is returned when the format includes subtype=mapml. The viewer is an HTML document that includes a head section with a link to the stylesheet. The default viewer is a simple viewer that includes a link to the default stylesheet. | ||
A template can be created to insert links to whole stylesheet or actual stylesheet elements. | ||
We can do this by creating a file called ``mapml-head-viewer.ftl`` in the GeoServer data directory in the directory for the layer that we wish to append links to. For example we could create this file under ``workspaces/topp/states_shapefile/states``. To add stylesheet links and stylesheet elements, we enter the following text inside this new file: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be mapml-preview-head.ftl
|
||
GetMap XML Head Stylesheet Templating | ||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
MapML in XML format includes a map-head element that includes map-link elements to link to other resources, including map style variants. Additional map-link elements can be added to the map-head element by creating a mapml-head.ftl template in the GeoServer data directory in the directory for the layer we wish to append map-links to. For example we could create the head.ftl file under ``workspaces/tiger/poly_landmarks_shapefile/poly_landmarks``: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be workspaces/tiger/nyc/poly_landmarks
? But when I locate a mapml-head.ftl
in that directory per line 58 below, I don't see it in the output. Not sure if it's me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually it was me and the software: the output is created, but doesn't match the documentation on lines 79-82 below. What appears is this:
<map-link
href="http://localhost:8080/geoserver/topp/wms?REQUEST=GetMap&FORMAT=text%2Fmapml&SRS=MapML%3AWGS84&FORMAT_OPTIONS=%7BMAPML-WMS-FORMAT%3Dimage%2Fpng%7D&BBOX=SRSEnvelope%5B-124.73142200000001%20%3A%20-66.969849%2C%2024.955967%20%3A%2049.371735%5D&VERSION=1.3.0&STYLES=population&SERVICE=WMS&WIDTH=330&HEIGHT=768&LAYERS=states"
rel="style" title="templateinsertedstyle" />
<map-style>.bbox {display:none} .population-r1-s1{fill:#4DFF4D; fill-opacity:0.7}
.population-r2-s1{fill:#FF4D4D; fill-opacity:0.7} .population-r3-s1{fill:#4D4DFF;
fill-opacity:0.7} .population-r4-s1{stroke-opacity:1.0; stroke-dashoffset:0;
stroke-width:0.2; stroke:#000000; stroke-linecap:butt}
.polygon-r1-s1{stroke-opacity:3.0; stroke-dashoffset:4; stroke-width:2.0; fill:#AAAAAA;
fill-opacity:3.0; stroke:#DD0000; stroke-linecap:butt}</map-style>
So the styles are merged, and the comments don't appear. I think that the template should create a new <map-style> but maybe this is something to do with marshaling or unmarshaling I can never remember which is which.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In mapml-features-head.ftl I also notice that the <map-style> elements in the template are merged into the end of the <map-style> that is created, at minimum creating some variance with the documentation.
|
||
A more complex example mapml-feature.ftl that operates on a multi-polygon layer and only inserts map-span tags into a specific feature:: | ||
|
||
<mapml- xmlns=\"http://www.w3.org/1999/xhtml\"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried the previous examples and got them to work, +/- any comments above. I think this example is the most complex so it would be ideal to make it work for one of the release layers, such as poly_landmarks. So far I haven't got it to have a visible effect though. One barrier is having to restart the server after each change to an .ftl file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried the previous examples and got them to work, +/- any comments above. I think this example is the most complex so it would be ideal to make it work for one of the release layers, such as poly_landmarks. So far I haven't got it to have a visible effect though. One barrier is having to restart the server after each change to an .ftl file.
I just pushed some udpates that should help:
1)There was an issue with the non-geospatial attributes not getting updated that should be resolved.
2)I have added several more examples, covering all of the geometry types
3)I fixed an issue with geometrycollection parsing and added an example to the documentation.
With regards to the requirement to restart when the template is updated - that is required for performance reasons. The template gets cached after the first time it gets used. I can change that but there will be a performance penalty. A possible compromise would be to hash the template and then monitor for changes - another performance penalty, but smaller than replacing it with every call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With regards to the requirement to restart when the template is updated - that is required for performance reasons.
I can live with it. It would be great if there was a debugging window in GeoServer where you could paste a ftl and see if it worked before saving it somewhere.
I tried this template from your updated examples with poi:
<mapml- xmlns="http://www.w3.org/1999/xhtml">
<map-head>
</map-head>
<map-body>
<map-feature>
<#list attributes as attribute>
<#if attribute.isGeometry>
<map-geometry>
<map-geometrycollection>
<map-linestring><map-coordinates><#list attribute.rawValue.coordinates as coord>${coord.x} ${coord.y}</#list></map-coordinates></map-linestring>
<map-point><map-coordinates><#list attribute.rawValue.coordinates as coord>${coord.x} ${coord.y}</#list></map-coordinates></map-point>
</map-geometrycollection>
</map-geometry>
</#if>
</#list>
</map-feature>
</map-body>
</mapml->
And I get this exception:
<?xml version="1.0" encoding="UTF-8"?><ServiceExceptionReport version="1.3.0" xmlns="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/ogc http://localhost:8080/geoserver/schemas/wms/1.3.0/exceptions_1_3_0.xsd"> <ServiceException>
javax.xml.bind.UnmarshalException
- with linked exception:
[Exception [EclipseLink-25004] (Eclipse Persistence Services - 2.7.4.v20190115-ad5b7c6b2a): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred unmarshalling the document
Internal Exception: org.xml.sax.SAXParseException; lineNumber: 9; columnNumber: 15; unexpected element (uri:"http://www.w3.org/1999/xhtml", local:"map-point"). Expected elements are <{http://www.w3.org/1999/xhtml}point>,<{http://www.w3.org/1999/xhtml}map-linestring>,<{http://www.w3.org/1999/xhtml}map-polygon>,<{http://www.w3.org/1999/xhtml}map-multipoint>,<{http://www.w3.org/1999/xhtml}map-multilinestring>,<{http://www.w3.org/1999/xhtml}map-multipolygon>]
Exception Description: An error occurred unmarshalling the document
Internal Exception: org.xml.sax.SAXParseException; lineNumber: 9; columnNumber: 15; unexpected element (uri:"http://www.w3.org/1999/xhtml", local:"map-point"). Expected elements are <{http://www.w3.org/1999/xhtml}point>,<{http://www.w3.org/1999/xhtml}map-linestring>,<{http://www.w3.org/1999/xhtml}map-polygon>,<{http://www.w3.org/1999/xhtml}map-multipoint>,<{http://www.w3.org/1999/xhtml}map-multilinestring>,<{http://www.w3.org/1999/xhtml}map-multipolygon>
unexpected element (uri:"http://www.w3.org/1999/xhtml", local:"map-point"). Expected elements are <{http://www.w3.org/1999/xhtml}point>,<{http://www.w3.org/1999/xhtml}map-linestring>,<{http://www.w3.org/1999/xhtml}map-polygon>,<{http://www.w3.org/1999/xhtml}map-multipoint>,<{http://www.w3.org/1999/xhtml}map-multilinestring>,<{http://www.w3.org/1999/xhtml}map-multipolygon>
</ServiceException></ServiceExceptionReport>
I like the fact that the examples are tied to specific release layers so I can work from an example that I know is supposed to work (my freemarker skills are what any beginner would be capable of).
For that error, try grabbing the most recent code from the PR branch.
There was an error in the GeometryCollection annotation that I corrected.
…On Mon, Jul 15, 2024 at 4:22 PM Peter Rushforth ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In doc/en/user/source/extensions/mapml/template.rst
<#368 (comment)>
:
> + </map-geometry>
+ </#if>
+ </#list>
+ </map-feature>
+ </map-body>
+ </mapml->
+
+This would result in a MapML feature output body that would resemble:
+
+ <mapml- xmlns="http://www.w3.org/1999/xhtml"><map-head><map-title>Bridges</map-title><map-meta charset="UTF-8"/><map-meta content="text/mapml" http-equiv="Content-Type"/><map-meta name="extent" content="top-left-longitude=-180.000000,top-left-latitude=90.000000,bottom-right-longitude=180.000000,bottom-right-latitude=-90.000000"/><map-meta name="cs" content="gcrs"/><map-meta name="projection" content="MapML:urn:ogc:def:crs:EPSG::4326"/><map-style>.bbox {display:none} .Bridges-r1-s1{r:48.0; stroke-opacity:1.0; stroke-dashoffset:0; well-known-name:square; stroke-width:1.0; opacity:1.0; fill:#808080; fill-opacity:1.0; stroke:#000000; stroke-linecap:butt}</map-style></map-head><map-body><map-feature id="Bridges.1107531599613" class="Bridges-r1-s1"><map-geometry><map-point><map-coordinates><map-span class="desired">0.0007 0.0002</map-span></map-coordinates></map-point></map-geometry><map-properties><table xmlns="http://www.w3.org/1999/xhtml"><thead><tr><th role="columnheader" scope="col">Property name</th><th role="columnheader" scope="col">Property value</th></tr></thead><tbody><tr><th scope="row">FID</th><td itemprop="FID">110</td></tr><tr><th scope="row">UPDATED NAME</th><td itemprop="NAME">CHANGED Cam Bridge</td></tr></tbody></table></map-properties></map-feature></map-body></mapml->
+
+Note that in addition to tagging the coordinates with a style class, the template also changes the name of the NAME property to "UPDATED NAME" and the value to "CHANGED Cam Bridge".
+
+A more complex example mapml-feature.ftl that operates on a multi-polygon layer and only inserts map-span tags into a specific feature::
+
+ <mapml- xmlns=\"http://www.w3.org/1999/xhtml\">
With regards to the requirement to restart when the template is updated -
that is required for performance reasons.
I can live with it. It would be great if there was a debugging window in
GeoServer where you could paste a ftl and see if it worked before saving it
somewhere.
I tried this template from your updated examples with poi:
<mapml- xmlns="http://www.w3.org/1999/xhtml">
<map-head>
</map-head>
<map-body>
<map-feature>
<#list attributes as attribute>
<#if attribute.isGeometry>
<map-geometry>
<map-geometrycollection>
<map-linestring><map-coordinates><#list attribute.rawValue.coordinates as coord>${coord.x} ${coord.y}</#list></map-coordinates></map-linestring>
<map-point><map-coordinates><#list attribute.rawValue.coordinates as coord>${coord.x} ${coord.y}</#list></map-coordinates></map-point>
</map-geometrycollection>
</map-geometry>
</#if>
</#list>
</map-feature>
</map-body>
</mapml->
And I get this exception:
<?xml version="1.0" encoding="UTF-8"?><ServiceExceptionReport version="1.3.0" xmlns="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/ogc http://localhost:8080/geoserver/schemas/wms/1.3.0/exceptions_1_3_0.xsd"> <ServiceException>
javax.xml.bind.UnmarshalException
- with linked exception:
[Exception [EclipseLink-25004] (Eclipse Persistence Services - 2.7.4.v20190115-ad5b7c6b2a): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred unmarshalling the document
Internal Exception: org.xml.sax.SAXParseException; lineNumber: 9; columnNumber: 15; unexpected element (uri:"http://www.w3.org/1999/xhtml", local:"map-point"). Expected elements are <{http://www.w3.org/1999/xhtml}point>,<{http://www.w3.org/1999/xhtml}map-linestring>,<{http://www.w3.org/1999/xhtml}map-polygon>,<{http://www.w3.org/1999/xhtml}map-multipoint>,<{http://www.w3.org/1999/xhtml}map-multilinestring>,<{http://www.w3.org/1999/xhtml}map-multipolygon>]
Exception Description: An error occurred unmarshalling the document
Internal Exception: org.xml.sax.SAXParseException; lineNumber: 9; columnNumber: 15; unexpected element (uri:"http://www.w3.org/1999/xhtml", local:"map-point"). Expected elements are <{http://www.w3.org/1999/xhtml}point>,<{http://www.w3.org/1999/xhtml}map-linestring>,<{http://www.w3.org/1999/xhtml}map-polygon>,<{http://www.w3.org/1999/xhtml}map-multipoint>,<{http://www.w3.org/1999/xhtml}map-multilinestring>,<{http://www.w3.org/1999/xhtml}map-multipolygon>
unexpected element (uri:"http://www.w3.org/1999/xhtml", local:"map-point"). Expected elements are <{http://www.w3.org/1999/xhtml}point>,<{http://www.w3.org/1999/xhtml}map-linestring>,<{http://www.w3.org/1999/xhtml}map-polygon>,<{http://www.w3.org/1999/xhtml}map-multipoint>,<{http://www.w3.org/1999/xhtml}map-multilinestring>,<{http://www.w3.org/1999/xhtml}map-multipolygon>
</ServiceException></ServiceExceptionReport>
I like the fact that the examples are tied to specific release layers so I
can work from an example that I know is supposed to work (my freemarker
skills are what any beginner would be capable of).
—
Reply to this email directly, view it on GitHub
<#368 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AHRJZ4SPJ7YFHCOBLZODYTLZMQVRNAVCNFSM6AAAAABITJVSU2VHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHMZDCNZYGU3TKNZXGI>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
ok this may be progress, or maybe not. I am trying to get the polygon / span template working on the nyc poly_landmarks layer working. I have this template so far, changed from your original example: <mapml- xmlns="http://www.w3.org/1999/xhtml">
<map-head>
</map-head>
<map-body>
<map-feature>
<#if attributes.LAND.value == "66.0">
<#list attributes as attribute>
<#if attribute.isGeometry>
<map-geometry>
<map-multipolygon>
<#list 0 ..< attribute.rawValue.getNumGeometries() as index>
<#assign polygon = attribute.rawValue.getGeometryN(index)>
<map-polygon>
<#assign shell = polygon.getExteriorRing()><map-coordinates><#list shell.coordinates as coord><#if coord?index == 0><map-span class="desired">${coord.x} ${coord.y}<#elseif coord?index == 4> ${coord.x} ${coord.y}</map-span><#else> ${coord.x} ${coord.y}</#if></#list></map-coordinates>
<#list 0 ..< polygon.getNumInteriorRing() as index>
<#assign hole = polygon.getInteriorRingN(index)><map-coordinates><#list hole.coordinates as coord><#if coord?index == 0><map-span class="desired">${coord.x} ${coord.y} <#elseif coord?index == 4> ${coord.x} ${coord.y}</map-span><#else> ${coord.x} ${coord.y}</#if></#list></map-coordinates></#list>
</map-polygon>
</#list>
</map-multipolygon>
</map-geometry>
</#if>
</#list>
<#else>
<#list attributes as attribute>
<#if attribute.isGeometry>
<map-geometry>
<map-multipolygon>
<#list 0 ..< attribute.rawValue.getNumGeometries() as index>
<#assign polygon = attribute.rawValue.getGeometryN(index)>
<map-polygon>
<#assign shell = polygon.getExteriorRing()><map-coordinates><#list shell.coordinates as coord> ${coord.x} ${coord.y} </#list></map-coordinates>
<#list 0 ..< polygon.getNumInteriorRing() as index>
<#assign hole = polygon.getInteriorRingN(index)><map-coordinates><#list hole.coordinates as coord> ${coord.x} ${coord.y} </#list></map-coordinates></#list>
</map-polygon>
</#list>
</map-multipolygon>
</map-geometry>
</#if>
</#list>
</#if>
</map-feature>
</map-body>
</mapml-> That template is looking for a particular polygon that has the attribute name "LAND" whose value is "66.0". I know this is a multipolygon, so should perhaps go through this branch. However, the response is as follows, I think showing that the attribute test is working, but there's a unmarshal error: <?xml version="1.0" encoding="UTF-8"?><ServiceExceptionReport version="1.3.0" xmlns="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/ogc http://localhost:8080/geoserver/schemas/wms/1.3.0/exceptions_1_3_0.xsd"> <ServiceException>
org.geoserver.platform.ServiceException: javax.xml.bind.UnmarshalException
- with linked exception:
[Exception [EclipseLink-25004] (Eclipse Persistence Services - 2.7.4.v20190115-ad5b7c6b2a): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred unmarshalling the document
Internal Exception: com.ctc.wstx.exc.WstxParsingException: Unexpected close tag </map-coordinates>; expected </map-span>.
at [row,col {unknown-source}]: [13,154]]
javax.xml.bind.UnmarshalException
- with linked exception:
[Exception [EclipseLink-25004] (Eclipse Persistence Services - 2.7.4.v20190115-ad5b7c6b2a): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred unmarshalling the document
Internal Exception: com.ctc.wstx.exc.WstxParsingException: Unexpected close tag </map-coordinates>; expected </map-span>.
at [row,col {unknown-source}]: [13,154]]
Exception Description: An error occurred unmarshalling the document
Internal Exception: com.ctc.wstx.exc.WstxParsingException: Unexpected close tag </map-coordinates>; expected </map-span>.
at [row,col {unknown-source}]: [13,154]
Unexpected close tag </map-coordinates>; expected </map-span>.
at [row,col {unknown-source}]: [13,154]
</ServiceException></ServiceExceptionReport> wdyt? |
In my version of the sample data, I was unable to find LAND=66.0, but I did find LAND=72.0. When I used the template you created with that value and the most current branch it worked for me when I tested with this URL: Also, FYI I changed the error handling to return the full template output string as part of the error, so it should be a bit easier to debug things. |
I've been messing around with the template to process the outer polygon example: <mapml- xmlns="http://www.w3.org/1999/xhtml">
<map-head>
</map-head>
<map-body>
<map-feature>
<#if attributes.LANAME.value == "Central Park">
<#list attributes as attribute>
<#if attribute.isGeometry>
<map-geometry>
<map-multipolygon>
<#list 0 ..< attribute.rawValue.getNumGeometries() as index>
<#assign polygon = attribute.rawValue.getGeometryN(index)>
<map-polygon>
<#assign shell = polygon.getExteriorRing()>
<map-coordinates><#-- note that having spaces around coordinates is VITAL -->
<map-span class="desired"><#list shell.coordinates as coord> ${coord.x} ${coord.y} </#list></map-span>
</map-coordinates>
<#list 0 ..< polygon.getNumInteriorRing() as index>
<#assign hole = polygon.getInteriorRingN(index)><map-coordinates><#list hole.coordinates as coord> ${coord.x} ${coord.y} </#list></map-coordinates></#list>
</map-polygon>
</#list>
</map-multipolygon>
</map-geometry>
</#if>
</#list>
<#else>
<#list attributes as attribute>
<#if attribute.isGeometry>
<map-geometry>
<map-multipolygon>
<#list 0 ..< attribute.rawValue.getNumGeometries() as index>
<#assign polygon = attribute.rawValue.getGeometryN(index)>
<map-polygon>
<#assign shell = polygon.getExteriorRing()><map-coordinates><#list shell.coordinates as coord> ${coord.x} ${coord.y} </#list></map-coordinates>
<#list 0 ..< polygon.getNumInteriorRing() as index>
<#assign hole = polygon.getInteriorRingN(index)><map-coordinates><#list hole.coordinates as coord> ${coord.x} ${coord.y} </#list></map-coordinates></#list>
</map-polygon>
</#list>
</map-multipolygon>
</map-geometry>
</#if>
</#list>
</#if>
</map-feature>
</map-body>
</mapml-> I noticed that the ftl may not get a hold of the polygon when it contains a <map-span class="bbox">...</map-span>, for example when you zoom in on central park for the above template, the Also, I wanted to try inserting a <map-a href> but I had to update the JAXB annotations to enable that. It worked, but then the client ran into problems processing the <map-a> for reasons unrelated to GeoServer (I think). Anyway, just to let you know that I'm still looking at this. |
Good catch on the number of decimals. The template is generating the coordinates directly, bypassing the existing formatting. The latest push corrects that by applying the formatting to the template output. With regards to the "bbox", that was a specific choice I made to skip the template insertion for features with the "bbox" class assigned. My thinking is that those geometries are specially reserved due to their presence on the periphery for tiling purposes? If that is not a correct interpretation, should I be merging the span classes to include both in those cases? |
I'm unsure. Might need a need a way to detect map-span in the template, if
that was even possible.?
P
…On Thu, Jul 18, 2024, 11:43 AM Joe ***@***.***> wrote:
I've been messing around with the template to process the outer polygon
example:
<mapml- xmlns="http://www.w3.org/1999/xhtml">
<map-head>
</map-head>
<map-body>
<map-feature>
<#if attributes.LANAME.value == "Central Park">
<#list attributes as attribute>
<#if attribute.isGeometry>
<map-geometry>
<map-multipolygon>
<#list 0 ..< attribute.rawValue.getNumGeometries() as index>
<#assign polygon = attribute.rawValue.getGeometryN(index)>
<map-polygon>
<#assign shell = polygon.getExteriorRing()>
<map-coordinates><#-- note that having spaces around coordinates is VITAL -->
<map-span class="desired"><#list shell.coordinates as coord> ${coord.x} ${coord.y} </#list></map-span>
</map-coordinates>
<#list 0 ..< polygon.getNumInteriorRing() as index>
<#assign hole = polygon.getInteriorRingN(index)><map-coordinates><#list hole.coordinates as coord> ${coord.x} ${coord.y} </#list></map-coordinates></#list>
</map-polygon>
</#list>
</map-multipolygon>
</map-geometry>
</#if>
</#list>
<#else>
<#list attributes as attribute>
<#if attribute.isGeometry>
<map-geometry>
<map-multipolygon>
<#list 0 ..< attribute.rawValue.getNumGeometries() as index>
<#assign polygon = attribute.rawValue.getGeometryN(index)>
<map-polygon>
<#assign shell = polygon.getExteriorRing()><map-coordinates><#list shell.coordinates as coord> ${coord.x} ${coord.y} </#list></map-coordinates>
<#list 0 ..< polygon.getNumInteriorRing() as index>
<#assign hole = polygon.getInteriorRingN(index)><map-coordinates><#list hole.coordinates as coord> ${coord.x} ${coord.y} </#list></map-coordinates></#list>
</map-polygon>
</#list>
</map-multipolygon>
</map-geometry>
</#if>
</#list>
</#if>
</map-feature>
</map-body>
</mapml->
I noticed that the ftl may not get a hold of the polygon when it contains
a ..., for example when you zoom in on central park for the above template,
the desired class is not rendered. Also, I noticed that the WFS setting
for number of decimal places is disregarded (not sure if it's in all
circumstances, but for sure when the ftl gets the polygon it forgets about
decimal places).
Also, I wanted to try inserting a but I had to update the JAXB annotations
to enable that. It worked, but then the client ran into problems processing
the for reasons unrelated to GeoServer (I think). Anyway, just to let you
know that I'm still looking at this.
Good catch on the number of decimals. The template is generating the
coordinates directly, bypassing the existing formatting. The latest push
corrects that by applying the formatting to the template output. With
regards to the "bbox", that was a specific choice I made to skip the
template insertion for features with the "bbox" class assigned. My thinking
is that those geometries are specially reserved due to their presence on
the periphery for tiling purposes? If that is not a correct interpretation,
should I be merging the span classes to include both in those cases?
—
Reply to this email directly, view it on GitHub
<#368 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AASTBJMEYBMT76DKTD6XXELZM7PB7AVCNFSM6AAAAABITJVSU2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDEMZWHA4TIMZRGI>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had a go at documentation and glanced over the code.
As a potential user of this, I really like the ability to customize links, HTML CSS, MapML CSS, and the feature attributes.
One thing that I think would be pretty interesting, is using the templates to push the styling beyond what the SLD translator can do. I'm not taking about the map-span here, but about templates that would create CSS for the features, and then perform logic, checks, on the feature attributes, and decide which CSS classes to apply a feature as a consequence.... it would be more powerful than SLD itself, full programmatic style control!
However, I'm not sure the implementation allows this use case, I see it grabbing the attributes and the geometry, but not the feature style.
At the same time, if one is going to use templates to do advanced, programmatic styling, having to re-encode also the attributes looks painful.
In general, dealing with geometries encoding is a lot of work... which is not surprising, it's already a lot of work to do GetFeaureInfo HTML templates, and all they do is to create a simple HTML table.... here we are at different level of complexity.
In terms of adding styles to clipped geometries... the clipping output of a polygon is normally stored in a geometry user data, but it's not a Polygon anymore, because the different sub-spans cannot be represented in JTS geometric anymore... they are a set of objects called TaggedPolygon, TaggedLineString and TaggedCoordinateSequence... I guess they could be used in a template too, but it would make the logic to build mapml even harder. Are we sure there are users willing to go there? Personally I already got lost in the polygon examples as they are...
<!-- End of added from the template --> | ||
</head> | ||
|
||
GetMap XML Head Stylesheet Templating |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about the usage "XML", I'd call it MapML. MapML is a set of tag extensions to HTML, which can be also valid XML, when written as XHTML, but it's not always so.
GetMap XML Head Stylesheet Templating | |
GetMap MapML Head Stylesheet Templating |
If you agree wit this change, please remove all other XML references as well.
|
||
GetMap XML Head Stylesheet Templating | ||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
MapML in XML format includes a map-head element that includes map-link elements to link to other resources, including map style variants. Additional map-link elements can be added to the map-head element by creating a mapml-head.ftl template in the GeoServer data directory in the directory for the layer we wish to append map-links to. For example we could create the mapml-head.ftl file under ``workspaces/tiger/nyc/poly_landmarks_shapefile/poly_landmarks``: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MapML in XML format includes a map-head element that includes map-link elements to link to other resources, including map style variants. Additional map-link elements can be added to the map-head element by creating a mapml-head.ftl template in the GeoServer data directory in the directory for the layer we wish to append map-links to. For example we could create the mapml-head.ftl file under ``workspaces/tiger/nyc/poly_landmarks_shapefile/poly_landmarks``: | |
MapML in XML format includes a map-head element that includes map-link elements to link to other resources, including map style variants. Additional map-link elements can be added to the map-head element by creating a ``mapml-head.ftl`` template in the GeoServer data directory in the directory for the layer we wish to append map-links to. For example we could create the ``mapml-head.ftl`` file under ``workspaces/tiger/nyc/poly_landmarks_shapefile/poly_landmarks``: |
The template names should be formatted as code. Also, it would be nice to have a summary table showing the template file names and their general usage (rather than having to go and search ".ftl" in the page)
<map-geometry> | ||
<map-point> | ||
<map-coordinates><#list gattribute.rawValue.coordinates as coord> | ||
<#if coord?index == 0><map-span class="desired">${coord.x} ${coord.y}</map-span><#else>${coord.x} ${coord.y}</#if></#list></map-coordinates></map-point> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using a point as an example... coord?index can only be 0 or not? Mabe better to use a linestring?
|
||
This would result in a MapML feature output body that would resemble:: | ||
|
||
<mapml- xmlns="http://www.w3.org/1999/xhtml"><map-head><map-title>Bridges</map-title><map-meta charset="UTF-8"/><map-meta content="text/mapml" http-equiv="Content-Type"/><map-meta name="extent" content="top-left-longitude=-180.000000,top-left-latitude=90.000000,bottom-right-longitude=180.000000,bottom-right-latitude=-90.000000"/><map-meta name="cs" content="gcrs"/><map-meta name="projection" content="MapML:urn:ogc:def:crs:EPSG::4326"/><map-style>.bbox {display:none} .Bridges-r1-s1{r:48.0; stroke-opacity:1.0; stroke-dashoffset:0; well-known-name:square; stroke-width:1.0; opacity:1.0; fill:#808080; fill-opacity:1.0; stroke:#000000; stroke-linecap:butt}</map-style></map-head><map-body><map-feature id="Bridges.1107531599613" class="Bridges-r1-s1"><map-geometry><map-point><map-coordinates><map-span class="desired">0.0007 0.0002</map-span></map-coordinates></map-point></map-geometry><map-properties><table xmlns="http://www.w3.org/1999/xhtml"><thead><tr><th role="columnheader" scope="col">Property name</th><th role="columnheader" scope="col">Property value</th></tr></thead><tbody><tr><th scope="row">FID</th><td itemprop="FID">110</td></tr><tr><th scope="row">UPDATED NAME</th><td itemprop="NAME">CHANGED Cam Bridge</td></tr></tbody></table></map-properties></map-feature></map-body></mapml-> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part should really be pretty-printed
<map-geometry> | ||
<map-polygon> | ||
<#assign shell = attribute.rawValue.getExteriorRing()><map-coordinates><#list shell.coordinates as coord><#if coord?index == 0><map-span class="desired">${coord.x} ${coord.y}<#elseif coord?index == 3> ${coord.x} ${coord.y}</map-span><#else> ${coord.x} ${coord.y}</#if></#list></map-coordinates> | ||
<#list 0 ..< attribute.rawValue.getNumInteriorRing() as index> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<#list 0 ..< attribute.rawValue.getNumInteriorRing() as index> | |
<#list 0 ..< attribute.rawValue.getNumInteriorRing() as index> |
This bit syntax is unfamiliar to me... is it actually working? Or was it meant to be
<#list 0 ..attribute.rawValue.getNumInteriorRing() as index>
<map-polygon> | ||
<#assign shell = attribute.rawValue.getExteriorRing()><map-coordinates><#list shell.coordinates as coord><#if coord?index == 0><map-span class="desired">${coord.x} ${coord.y}<#elseif coord?index == 3> ${coord.x} ${coord.y}</map-span><#else> ${coord.x} ${coord.y}</#if></#list></map-coordinates> | ||
<#list 0 ..< attribute.rawValue.getNumInteriorRing() as index> | ||
<#assign hole = attribute.rawValue.getInteriorRingN(index)><map-coordinates><#list hole.coordinates as coord><#if coord?index == 0><map-span class="desired">${coord.x} ${coord.y} <#elseif coord?index == 3> ${coord.x} ${coord.y}</map-span><#else> ${coord.x} ${coord.y}</#if></#list></map-coordinates></#list> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quite packed and nested... can it be unfolded a bit for readability, or the extra spaces would break parsing? Most of the examples seem to suffer from the same issue.
Since we are parsing it back, the generated XML does not need to be "pretty", as long as it's valid.
throws IOException { | ||
if (mapMLStyles != null && mapMLStyles.isEmpty()) { | ||
// no applicable styles, probably because of scale | ||
return Optional.empty(); | ||
} | ||
Feature f = new Feature(); | ||
f.setId(sf.getID()); | ||
Optional<Map<String, String>> replacmentAttsOptional = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional<Map<String, String>> replacmentAttsOptional = | |
Optional<Map<String, String>> replacementAttsOptional = |
|
||
private static Optional<Map<String, String>> getTemplateAttributes( | ||
Optional<Mapml> templateOptional) { | ||
if (templateOptional.isPresent()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ouch, this method is not using the one thing I like most about Optional: avoiding deep nested checks over chained calls.
I've asked ChatGPT to simplify it so that it uses Otional's map and filter, I'm not sure it's correct, but it certainly looks as I would expect (more compact and I believe more readable too):
private static Optional<Map<String, String>> getTemplateAttributes(
Optional<Mapml> templateOptional) {
return templateOptional
.map(Mapml::getBody)
.map(Body::getFeatures)
.filter(features -> features != null && !features.isEmpty())
.map(features -> features.get(0))
.map(Feature::getProperties)
.map(Properties::getOtherAttributes)
.filter(attributes -> attributes != null && !attributes.isEmpty() && attributes.values().size() % 2 == 0)
.map(attributes -> {
List<String> values = new ArrayList<>(attributes.values());
return IntStream.range(0, values.size() / 2)
.boxed()
.collect(Collectors.toMap(i -> values.get(i * 2), i -> values.get(i * 2 + 1)));
});
}
/** Collects the attributes representation as a HTML table */ | ||
private String collectAttributes(SimpleFeature sf) { | ||
private String collectAttributes( | ||
SimpleFeature sf, Optional<Map<String, String>> replacmentAttsOptional) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SimpleFeature sf, Optional<Map<String, String>> replacmentAttsOptional) { | |
SimpleFeature sf, Optional<Map<String, String>> replacementAttsOptional) { |
I think this sounds like a good direction. I am not knowledgeable enough about SLD to know where its limits are, but having the ability to encode procedural logic to derive class values sounds like what I would expect of the (ftl) templating system.
If it's simple to achieve this, let's add it in. If not, let's skip it for now. Either way, we can then let the whole templating feature content MapML extension feature mature so that we can use it / play with it until we know exactly where we want to go with it, and then do that as future work.
Understood, this is why I'm working on making example ftl files that work with a specific data set that geoserver users have access to (the release data directory). I think if a user can take an example that she can see works on data set A, and apply it to data set Z that she owns, that is one way of getting users up to speed. As previously discussed, it would be ideal (in the future) to be able to generate ftl files from the output of an interactive graphic environment such as from CSV encoding of a specific segment to hyperlink or style or otherwise mark up etc. Even a combination of generated and then manually edited ftl content could be interesting.
That's pretty cool actually!
To avoid having to mess with what the representation of the geometry (part) is (i.e. TaggedPolygon et al), we will need to be able to reliably wrap all the coordinates of the <map-coordinates> in a <map-span> (or <map-a>) regardless of whether there's internal structure or not. We might avoid having to use <map-span> to wrap whole geometry parts (but not be able to avoid <map-a>) by supporting the
Pretty printing might help. Knowing / documenting the data model available, and how to manipulate it would help. Working examples of planned / supported use cases would help. But yes, if we don't have to go there (exposing TaggedPolygon et al to the ftl data model), I think it would be better to avoid it until we have found what users / we want to do with it. |
Thinking more about this. The geometry is never going to be consistent. Joe's approach of filtering out the tagged geometries was appropriate, but I think manipulating coordinates in the ftl is is bound to not work anyway because the geometry is mostly often going to be chopped up. However I would like to achieve wrapping the whole geometry, tagged or not, in a map-a, and potentially calculating a class value for it based on attributes. In short I think we can't expect to add links or spans to sub geometries because of panning and zooming making those geometries too unreliable. |
I can make changes to the documentation examples to reflect this approach - using the FreeMarker template to wrap selected whole geometries in map-a tags. How would you envision the mapping of the class values from the attributes? Would it be logic in the FTL using "if-else" tags to specify a "map-a" tag in with "class='foo'" in place and "class='bar'" in another? |
I think, because the state of the geometry internally depends on whether or not it is clipped by the viewport or tile, that we will have to be able to have / target a I know that wrapping a map-geometry in a map-a will work, because we have examples of that working here for example. Edit: Here's how the linked example of <map-a> works, which is by wrapping the content of the <map-geometry> in a <map-a>: <map-geometry>
<map-a href="#11,-123.362105,48.430882">
<map-point>
<map-coordinates>-123.362105 48.430882</map-coordinates>
</map-point>
</map-a>
</map-geometry> As for how to template the tagged geometries, we'll have to think on that. It would be nice to have but not a show stopper, and we don't want to make it so complicated that it's a barrier. |
Peter - I have added the "map-a" wrapper to the JaxB definitions in the most recent push to this PR and tried the approach you described but I the javascript associated with the preview viewer doesn't seem to be able to handle it? See below for the template I used with the NYC TIGER POI sample dataset: |
@prushforth Should I continue forward with documenting (and unit tests) that demonstrate the map-a wrapper approach? Is there a new version of the JavaScript client that supports map-a? Thanks. |
@prushforth Here is the template I used (the one in the example above did not copy correctly):
|
Hi Joe, sorry for not replying earlier. I had this on my list to look at today, and will proceed with that plan now. Thanks for your patience. |
@turingtestfail Joe I'm having difficulty building and running this branch now - could you make it so that I can just clone it and build it? I have to merge main and I'm not sure how to resolve the conflicts I'm getting. |
Specifically, while I can successfully build this branch, I can't deploy it, as GeoServer fails on startup with the following message:
Earlier, I successfully got around that by merging main into this branch, but now I get conflicts and I am not able to resolve them. At least, not yet. |
I have been able to get the server running using this process: Step 1: From your project repository, check out a new branch and test the changes. git checkout -b turingtestfail-GEOS-11368-MapML-FreeMarker main +/- some fudging with stuff. I confirm that there's a bug in the viewer code that prevents <map-a> from working with features loaded via <map-link rel=features>, and I'm working on that now. I think we can proceed in this direction anyway, and I will try to fix the bug. |
@prushforth so we are good for a public PR? |
Yes |
d31c0cc
to
a5a1c5c
Compare
started on template page ftl file names correction fixed xml code block and added map-property Created preview header integration test and started extracting template swapped in better method for empty feature finished first integration test switched to dedicated template head template style registering servicelink fixed servicelink changed serviceLink to base,path,kvp cleanup dollar signs breaking code block started on subfeature span insertion with integration test Got point xml interpolation working progressing on polygon Got polygon unmarshal working got multipolygon to unmarshal changed strings to coords and linestring parsing removed interpolated, created head styles using basic mapml attributes and point test working and phantom space removed polygon and multipolygon tests added feature id check to multipolygon test tests with space replacement version that uses lists of coordinates instead of strings removed some other remainder stuff from the space thing cleanup added check for tagged geom doc update PR changes, mainly map-span tests updated without xml escape updated documentation removing cdata escaping added template attributes to mapml feature fixed attribute replacement and added documentation examples more doc update line test fix fixed issue with geometrycollection and updated documentation with example better output if error format coordinates, including number of decimals fixed pmd PR doc updates and started on a wrapper fixed underline in template doc preview header change restored test remoe ipr iws simplified optional doc updates
8911220
to
8d7d7b6
Compare
started on template page
Checklist
main
branch (backports managed later; ignore for branch specific issues).For core and extension modules:
[GEOS-XYZWV] Title of the Jira ticket
.