Skip to content

Commit

Permalink
added template attributes to mapml feature
Browse files Browse the repository at this point in the history
  • Loading branch information
turingtestfail committed Jul 12, 2024
1 parent e37d8e2 commit 3f2e332
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 25 deletions.
17 changes: 7 additions & 10 deletions doc/en/user/source/extensions/mapml/template.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ MapML templates are written in `Freemarker <http://www.freemarker.org/>`_ , a Ja

GetMap MapML HTML Preview/Layer Preview Head Stylesheet Templating
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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.
The preview is returned when the format includes subtype=mapml. The preview is an HTML document that includes a head section with a link to the stylesheet. The default preview 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:
We can do this by creating a file called ``mapml-preview-head.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:

.. code-block:: html

Expand All @@ -25,7 +25,7 @@ This would result in a head section that would resemble:
.. code-block:: html

<head>
<title>World Map</title>
<title>USA Population</title>
<meta charset='utf-8'>
<script type="module" src="http://localhost:8080/geoserver/mapml/viewer/widget/mapml-viewer.js"></script>
<style>
Expand All @@ -51,7 +51,7 @@ This would result in a head section that would resemble:

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``:
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``:

.. code-block:: bash
Expand All @@ -60,7 +60,7 @@ MapML in XML format includes a map-head element that includes map-link elements
<map-link href="${serviceLink("${base}","${path}","${kvp}")}" rel="\${rel}" title="templateinsertedstyle"/>
<!-- End of added from the template -->
This would result in a map-head section that would resemble:
This would result in a map-head section that would resemble (note the inserted css styles and map-link):

.. code-block:: html

Expand All @@ -75,11 +75,8 @@ This would result in a map-head section that would resemble:
<map-link href="http://localhost:8080/geoserver/tiger/wms?format_options=mapml-wms-format%3Aimage%2Fpng&amp;request=GetMap&amp;crs=MapML%3AOSMTILE&amp;service=WMS&amp;bbox=-2.0037508342789244E7%2C-2.364438881673656E7%2C2.0037508342789244E7%2C2.364438881673657E7&amp;format=text%2Fmapml&amp;layers=poly_landmarks&amp;width=768&amp;version=1.3.0&amp;height=384" rel="alternate" projection="OSMTILE"/>
<map-link href="http://localhost:8080/geoserver/tiger/wms?format_options=mapml-wms-format%3Aimage%2Fpng&amp;request=GetMap&amp;crs=MapML%3ACBMTILE&amp;service=WMS&amp;bbox=-8079209.971443829%2C-3626624.322362231%2C8281691.192343056%2C1.233598344760506E7&amp;format=text%2Fmapml&amp;layers=poly_landmarks&amp;width=768&amp;version=1.3.0&amp;height=384" rel="alternate" projection="CBMTILE"/>
<map-link href="http://localhost:8080/geoserver/tiger/wms?format_options=mapml-wms-format%3Aimage%2Fpng&amp;request=GetMap&amp;crs=MapML%3AAPSTILE&amp;service=WMS&amp;bbox=-1.06373184982574E7%2C-1.06373184982574E7%2C1.46373184982574E7%2C1.46373184982574E7&amp;format=text%2Fmapml&amp;layers=poly_landmarks&amp;width=768&amp;version=1.3.0&amp;height=384" rel="alternate" projection="APSTILE"/>
<map-style>.bbox {display:none} .polygon-r1-s1{stroke-opacity:1.0; stroke-dashoffset:0; stroke-width:1.0; fill:#AAAAAA; fill-opacity:1.0; stroke:#000000; stroke-linecap:butt}</map-style>
<!-- Added from the template -->
<map-style>.polygon-r2-s2{stroke-opacity:3.0; stroke-dashoffset:4; stroke-width:2.0; fill:#AAAAAA; fill-opacity:3.0; stroke:#DD0000; stroke-linecap:butt}</map-style>
<map-link href="http://localhost:8080/geoserver/tiger/wms?format_options=mapml-wms-format%3Aimage%2Fpng&amp;request=GetMap&amp;crs=MapML%3AWGS84&amp;service=WMS&amp;bbox=-180.0%2C-90.0%2C180.0%2C90.0&amp;format=text%2Fmapml&amp;layers=poly_landmarks&amp;width=768&amp;styles=templateinsertedstyle&amp;version=1.3.0&amp;height=384" rel="style" title="templateinsertedstyle"/>
<!-- End of added from the template -->
<map-style>.bbox {display:none} .poly_landmarks-r1-s1{stroke-opacity:1.0; stroke-dashoffset:0; stroke-width:1.0; fill:#B4DFB4; fill-opacity:1.0; stroke:#88B588; stroke-linecap:butt} .poly_landmarks-r2-s1{stroke-opacity:1.0; stroke-dashoffset:0; stroke-width:1.0; fill:#8AA9D1; fill-opacity:1.0; stroke:#436C91; stroke-linecap:butt} .poly_landmarks-r3-s1{stroke-opacity:1.0; stroke-dashoffset:0; stroke-width:1.0; fill:#FDE5A5; fill-opacity:0.75; stroke:#6E6E6E; 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>
</map-head>

GetMap Features Inline Style Class Templating
Expand All @@ -88,7 +85,7 @@ MapML in XML feature format (when the parameter format_options=mapmlfeatures:tru
Within the map-body, map-feature elements include map-geometry with map-coordinates.

The mapml-feature-head.ftl is a file that can be used to insert map-style elements with the style class definitions.
This file is placed in the GeoServer data directory in the directory for the layer we wish to append style classes to. For example we could create the file under ``workspaces/tiger/poly_landmarks_shapefile/poly_landmarks``.
This file is placed in the GeoServer data directory in the directory for the layer we wish to append style classes to. For example we could create the file under ``workspaces/tiger/nyc/poly_landmarks_shapefile/poly_landmarks``.

The mapml-feature-head.ftl file would look like::

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,6 @@ public static Mapml featureCollectionToMapML(
Optional<Mapml> interpolatedOptional = Optional.empty();
if (hasTemplate) {
interpolatedOptional = getInterpolatedFromTemplate(fc, feature);
// appendTemplateCSSStyle(head, interpolatedOptional);
}
// convert feature to xml
if (styles != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import org.apache.commons.text.StringEscapeUtils;
import org.geoserver.mapml.xml.Coordinates;
import org.geoserver.mapml.xml.Feature;
Expand Down Expand Up @@ -44,6 +47,8 @@
import org.springframework.core.Constants;
import org.springframework.util.PropertyPlaceholderHelper;

import javax.xml.namespace.QName;

/**
* @author Chris Hodgson
* @author prushforth
Expand Down Expand Up @@ -85,6 +90,8 @@ public Optional<Feature> buildFeature(
}
Feature f = new Feature();
f.setId(sf.getID());
Optional<Map<String, String>> replacmentAttsOptional =
getTemplateAttributes(templateOptional);
if (featureCaptionTemplate != null && !featureCaptionTemplate.isEmpty()) {
AttributeValueResolver attributeResolver = new AttributeValueResolver(sf);
String caption =
Expand All @@ -98,7 +105,7 @@ public Optional<Feature> buildFeature(
if (!skipAttributes) {
PropertyContent pc = new PropertyContent();
f.setProperties(pc);
pc.setAnyElement(collectAttributes(sf));
pc.setAnyElement(collectAttributes(sf, replacmentAttsOptional));
}

// if clipping is enabled, clip the geometry and return null if the clip removed it entirely
Expand Down Expand Up @@ -159,29 +166,103 @@ public Optional<Feature> buildFeature(
return Optional.of(f);
}

private static Optional<Map<String, String>> getTemplateAttributes(
Optional<Mapml> templateOptional) {
if (templateOptional.isPresent()) {
if (templateOptional.get().getBody() != null
&& templateOptional.get().getBody().getFeatures() != null
&& templateOptional.get().getBody().getFeatures().size() > 0
&& templateOptional.get().getBody().getFeatures().get(0).getProperties() != null
&& templateOptional
.get()
.getBody()
.getFeatures()
.get(0)
.getProperties()
.getOtherAttributes()
!= null
&& !templateOptional
.get()
.getBody()
.getFeatures()
.get(0)
.getProperties()
.getOtherAttributes()
.isEmpty()
&& templateOptional // there needs to be an even number of values because they
// are actually key value pairs
.get()
.getBody()
.getFeatures()
.get(0)
.getProperties()
.getOtherAttributes()
.values()
.size()
% 2
== 0) {
List<String> values =
new ArrayList<>(
templateOptional
.get()
.getBody()
.getFeatures()
.get(0)
.getProperties()
.getOtherAttributes()
.values());
Map<String, String> attributePairs =
IntStream.range(0, values.size() / 2)
.boxed()
.collect(
Collectors.toMap(
i -> values.get(i * 2),
i -> values.get(i * 2 + 1)));
return Optional.of(attributePairs);
}
}
return Optional.empty();
}

/** Collects the attributes representation as a HTML table */
private String collectAttributes(SimpleFeature sf) {
private String collectAttributes(
SimpleFeature sf, Optional<Map<String, String>> replacmentAttsOptional) {
StringBuilder sb =
new StringBuilder(
"<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>");

for (AttributeDescriptor attr : sf.getFeatureType().getAttributeDescriptors()) {
if (!(attr.getType() instanceof GeometryType)) {
String escapedName = StringEscapeUtils.escapeXml10(attr.getLocalName());
String value =
sf.getAttribute(attr.getName()) != null
? sf.getAttribute(attr.getName()).toString()
: "";
if (replacmentAttsOptional.isEmpty()) {
for (AttributeDescriptor attr : sf.getFeatureType().getAttributeDescriptors()) {
if (!(attr.getType() instanceof GeometryType)) {
String escapedName = StringEscapeUtils.escapeXml10(attr.getLocalName());
String value =
sf.getAttribute(attr.getName()) != null
? sf.getAttribute(attr.getName()).toString()
: "";
sb.append("<tr><th scope=\"row\">")
.append(escapedName)
.append("</th>")
.append("<td itemprop=\"")
.append(escapedName)
.append("\">")
.append(StringEscapeUtils.escapeXml10(value))
.append("</td></tr>");
}
}
} else {
for (Map.Entry<String, String> entry : replacmentAttsOptional.get().entrySet()) {
String escapedName = StringEscapeUtils.escapeXml10(entry.getKey());
String value = StringEscapeUtils.escapeXml10(entry.getValue());
sb.append("<tr><th scope=\"row\">")
.append(escapedName)
.append("</th>")
.append("<td itemprop=\"")
.append(escapedName)
.append("\">")
.append(StringEscapeUtils.escapeXml10(value))
.append(value)
.append("</td></tr>");
}
}
Expand Down Expand Up @@ -580,7 +661,9 @@ private String buildCoordinates(List<Coordinate> cs) {
return joiner.toString();
}

/** @param numDecimals */
/**
* @param numDecimals
*/
public void setNumDecimals(int numDecimals) {
// make a copy of relevant object state
boolean fd = this.formatter.isForcedDecimal();
Expand All @@ -592,12 +675,16 @@ public void setNumDecimals(int numDecimals) {
this.formatter.setPadWithZeros(pad);
}

/** @param forcedDecimal */
/**
* @param forcedDecimal
*/
public void setForcedDecimal(boolean forcedDecimal) {
this.formatter.setForcedDecimal(forcedDecimal);
}

/** @param padWithZeros */
/**
* @param padWithZeros
*/
public void setPadWithZeros(boolean padWithZeros) {
this.formatter.setPadWithZeros(padWithZeros);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
import org.junit.Test;
import org.locationtech.jts.io.ParseException;

import javax.xml.namespace.QName;

public class MapMLWMSFeatureTest extends MapMLTestSupport {
@Override
protected void onSetUp(SystemTestData testData) throws Exception {
Expand Down Expand Up @@ -388,7 +390,9 @@ public void testMapMLFeaturePointHasClass() throws Exception {
mapmlFeatures
.getBody()
.getFeatures()
.get(0); // get the second feature, which has a class
.get(0); // get the first feature, which has a class
String attributes = feature2.getProperties().getAnyElement();
assertTrue(attributes.contains("UPDATED NAME"));
Point featurePoint = (Point) feature2.getGeometry().getGeometryContent().getValue();
Span span = ((Span) featurePoint.getCoordinates().get(0).getCoordinates().get(0));
assertEquals("desired", span.getClazz());
Expand Down

0 comments on commit 3f2e332

Please sign in to comment.