Skip to content

Commit

Permalink
MapML vector tiles, clipping on bbox and made extra sides transparent
Browse files Browse the repository at this point in the history
  • Loading branch information
aaime committed Mar 7, 2024
1 parent bbb2803 commit 8fae5a5
Show file tree
Hide file tree
Showing 19 changed files with 816 additions and 33 deletions.
2 changes: 1 addition & 1 deletion src/extension/mapml/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.0.0</version>
<version>3.1.1</version>
<configuration>
<excludes>**/org/geoserver/mapml/xml/**/*</excludes>
</configuration>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import org.geoserver.mapml.xml.Link;
import org.geoserver.mapml.xml.Mapml;
import org.geoserver.mapml.xml.Meta;
import org.geoserver.mapml.xml.MimeType;
import org.geoserver.mapml.xml.Option;
import org.geoserver.mapml.xml.PositionType;
import org.geoserver.mapml.xml.ProjType;
Expand Down Expand Up @@ -1213,7 +1214,13 @@ private void generateTiledWMSClientLinks(MapMLLayerMetadata mapMLLayerMetadata)
params.put("elevation", "{elevation}");
}
params.put("bbox", "{txmin},{tymin},{txmax},{tymax}");
params.put("format", imageFormat);
if (mapMLLayerMetadata.isUseFeatures()) {
params.put("format", MAPML_MIME_TYPE);
params.put("format_options", MAPML_FEATURE_FORMAT_OPTIONS);
tileLink.setType(MimeType.TEXT_MAPML);
} else {
params.put("format", imageFormat);
}
params.put("transparent", Boolean.toString(mapMLLayerMetadata.isTransparent()));
params.put("width", "256");
params.put("height", "256");
Expand Down Expand Up @@ -1625,7 +1632,8 @@ public String getMapMLHTMLDocument() {
escapeHtml4(proj),
styleName,
format))
.append("\" checked></layer->\n")
.append("\" checked>")
.append("</layer->\n")
.append("</mapml-viewer>\n")
.append("</body>\n")
.append("</html>");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,15 @@
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.metadata.iso.citation.Citations;
import org.geotools.referencing.CRS;
import org.locationtech.jts.geom.Envelope;

public class MapMLFeatureUtil {
/**
* Convert a feature collection to a MapML document
*
* @param featureCollection the feature collection to be converted to MapML
* @param layerInfo metadata for the feature class
* @param clipBounds the bounds to clip the features to (or null if not clipping is desired)
* @param requestCRS the CRS requested by the client
* @param alternateProjections alternate projections for the feature collection
* @param numDecimals number of decimal places to use for coordinates
Expand All @@ -55,6 +57,7 @@ public class MapMLFeatureUtil {
public static Mapml featureCollectionToMapML(
FeatureCollection featureCollection,
LayerInfo layerInfo,
Envelope clipBounds,
CoordinateReferenceSystem requestCRS,
List<Link> alternateProjections,
int numDecimals,
Expand Down Expand Up @@ -89,6 +92,9 @@ public static Mapml featureCollectionToMapML(
if (alternateProjections != null) {
links.addAll(alternateProjections);
}
if (clipBounds != null) {
head.setStyle(".bbox{display:none}\n.polygon{fill:yellow; stroke: orange}");
}

String licenseLink = layerMeta.get("mapml.licenseLink", String.class);
String licenseTitle = layerMeta.get("mapml.licenseTitle", String.class);
Expand Down Expand Up @@ -116,13 +122,16 @@ public static Mapml featureCollectionToMapML(
featureBuilder.setNumDecimals(numDecimals);
featureBuilder.setForcedDecimal(forcedDecimal);
featureBuilder.setPadWithZeros(padWithZeros);
featureBuilder.setClipBounds(clipBounds);
try (SimpleFeatureIterator iterator = fc.features()) {
while (iterator.hasNext()) {
SimpleFeature feature = iterator.next();
// convert feature to xml

Feature f = featureBuilder.buildFeature(feature, fCaptionTemplate);
features.add(f);
if (f != null) {
features.add(f);
}
}
}
return mapml;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ public Mapml getMapMLDocument() throws IOException {
return MapMLFeatureUtil.featureCollectionToMapML(
reprojectedFeatureCollection,
layerInfo,
getMapRequest.getBbox(), // clip on bound
crs,
null, // for WMS GetMap we don't include alternate projections
getNumberOfDecimals(meta),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,23 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringJoiner;
import javax.xml.bind.JAXBElement;
import org.apache.commons.text.StringEscapeUtils;
import org.geoserver.mapml.xml.Coordinates;
import org.geoserver.mapml.xml.Feature;
import org.geoserver.mapml.xml.GeometryContent;
import org.geoserver.mapml.xml.ObjectFactory;
import org.geoserver.mapml.xml.PropertyContent;
import org.geoserver.mapml.xml.Span;
import org.geotools.api.feature.simple.SimpleFeature;
import org.geotools.api.feature.type.AttributeDescriptor;
import org.geotools.api.feature.type.GeometryType;
import org.geotools.gml.producer.CoordinateFormatter;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.impl.CoordinateArraySequence;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.PlaceholderConfigurerSupport;
Expand All @@ -36,6 +42,7 @@ public class MapMLGenerator {

private int DEFAULT_NUM_DECIMALS = 8;
private CoordinateFormatter formatter = new CoordinateFormatter(DEFAULT_NUM_DECIMALS);
private Envelope clipBounds;

/**
* @param sf a feature
Expand Down Expand Up @@ -68,10 +75,10 @@ public Feature buildFeature(SimpleFeature sf, String featureCaptionTemplate)
+ "<th role=\"columnheader\" scope=\"col\">Property value</th>"
+ "</tr></thead><tbody>");

org.locationtech.jts.geom.Geometry g = null;
Geometry g = null;
for (AttributeDescriptor attr : sf.getFeatureType().getAttributeDescriptors()) {
if (attr.getType() instanceof GeometryType) {
g = (org.locationtech.jts.geom.Geometry) (sf.getAttribute(attr.getName()));
g = (Geometry) (sf.getAttribute(attr.getName()));
} else {
String escapedName = StringEscapeUtils.escapeXml10(attr.getLocalName());
String value =
Expand All @@ -90,11 +97,21 @@ public Feature buildFeature(SimpleFeature sf, String featureCaptionTemplate)
}
sb.append("</tbody></table>");
pc.setAnyElement(sb.toString());

// if clipping is enabled, clip the geometry and return null if the clip removed it entirely
if (g != null && !g.isEmpty() && clipBounds != null) {
MapMLGeometryClipper clipper = new MapMLGeometryClipper(g, clipBounds);
g = clipper.clipAndTag();
}
f.setGeometry(buildGeometry(g));
return f;
}

private class AttributeValueResolver {
public void setClipBounds(Envelope clipBounds) {
this.clipBounds = clipBounds;
}

private static class AttributeValueResolver {

private final Constants constants = new Constants(PlaceholderConfigurerSupport.class);
private final PropertyPlaceholderHelper helper =
Expand All @@ -107,6 +124,7 @@ private class AttributeValueResolver {
private final SimpleFeature feature;
private final PropertyPlaceholderHelper.PlaceholderResolver resolver =
(name) -> resolveAttributeNames(name);

/**
* Wrap the feature to caption via this constructor
*
Expand All @@ -115,6 +133,7 @@ private class AttributeValueResolver {
protected AttributeValueResolver(SimpleFeature feature) {
this.feature = feature;
}

/**
* Take an attribute name, return the attribute. "attribute" may be a band name. Band names
* can have spaces in them, but these seem to require that the space be replaced by an
Expand All @@ -130,6 +149,7 @@ private String resolveAttributeNames(String attributeName) {
if (attribute == null) return nullValue;
return feature.getAttribute(attributeName).toString();
}

/**
* Invokes PropertyPlaceholderHelper.replacePlaceholders, which iterates over the
* userTemplate string to replace placeholders with attribute values of the attribute of
Expand Down Expand Up @@ -186,6 +206,7 @@ public GeometryContent buildGeometry(org.locationtech.jts.geom.Geometry g) throw

return geom;
}

/**
* @param g a JTS Geometry
* @return
Expand All @@ -212,6 +233,7 @@ private Object buildSpecificGeom(org.locationtech.jts.geom.Geometry g) throws IO
throw new IOException("Unknown geometry type: " + g.getGeometryType());
}
}

/**
* @param gc a JTS GeometryCollection
* @return
Expand All @@ -227,6 +249,7 @@ private org.geoserver.mapml.xml.GeometryCollection buildGeometryCollection(
}
return geomColl;
}

/**
* @param mpg a JTS MultiPolygo
* @return
Expand All @@ -240,6 +263,7 @@ private org.geoserver.mapml.xml.MultiPolygon buildMultiPolygon(
}
return multiPoly;
}

/**
* @param ml a JTS MultiLineString
* @return
Expand All @@ -259,6 +283,7 @@ private org.geoserver.mapml.xml.MultiLineString buildMultiLineString(
}
return multiLine;
}

/**
* @param l a JTS LineString
* @return
Expand All @@ -270,6 +295,7 @@ private org.geoserver.mapml.xml.LineString buildLineString(
buildCoordinates(l.getCoordinateSequence(), lsCoords);
return lineString;
}

/**
* @param mp a JTS MultiPoint
* @return
Expand All @@ -281,6 +307,7 @@ private org.geoserver.mapml.xml.MultiPoint buildMultiPoint(
buildCoordinates(new CoordinateArraySequence(mp.getCoordinates()), mpCoords);
return multiPoint;
}

/**
* @param p a JTS Point
* @return
Expand All @@ -291,22 +318,70 @@ private org.geoserver.mapml.xml.Point buildPoint(org.locationtech.jts.geom.Point
.add(this.formatter.format(p.getX()) + " " + this.formatter.format(p.getY()));
return point;
}

/**
* @param p a JTS Polygon
* @return
*/
private org.geoserver.mapml.xml.Polygon buildPolygon(org.locationtech.jts.geom.Polygon p) {
if (p.getUserData() instanceof TaggedPolygon)
return buildTaggedPolygon((TaggedPolygon) p.getUserData());

org.geoserver.mapml.xml.Polygon poly = new org.geoserver.mapml.xml.Polygon();
List<JAXBElement<List<String>>> ringList = poly.getThreeOrMoreCoordinatePairs();
List<String> coordList =
buildCoordinates(p.getExteriorRing().getCoordinateSequence(), null);
ringList.add(factory.createPolygonCoordinates(coordList));
List<Coordinates> ringList = poly.getThreeOrMoreCoordinatePairs();
String coordList = buildCoordinates(p.getExteriorRing().getCoordinateSequence());
ringList.add(new Coordinates(coordList));
for (int i = 0; i < p.getNumInteriorRing(); i++) {
coordList = buildCoordinates(p.getInteriorRingN(i).getCoordinateSequence(), null);
ringList.add(factory.createPolygonCoordinates(coordList));
coordList = buildCoordinates(p.getInteriorRingN(i).getCoordinateSequence());
ringList.add(new Coordinates(coordList));
}
return poly;
}

private org.geoserver.mapml.xml.Polygon buildTaggedPolygon(TaggedPolygon taggedPolygon) {
org.geoserver.mapml.xml.Polygon poly = new org.geoserver.mapml.xml.Polygon();
List<Coordinates> ringList = poly.getThreeOrMoreCoordinatePairs();
ringList.add(new Coordinates(buildTaggedLinestring(taggedPolygon.getBoundary())));
for (TaggedPolygon.TaggedLineString hole : taggedPolygon.getHoles()) {
ringList.add(new Coordinates(buildTaggedLinestring(hole)));
}
return poly;
}

private List<Object> buildTaggedLinestring(TaggedPolygon.TaggedLineString ls) {
List<Object> result = new ArrayList<>();
List<TaggedPolygon.TaggedCoordinateSequence> coordinates = ls.getCoordinates();
int last = coordinates.size();
for (int i = 0; i < last; i++) {
TaggedPolygon.TaggedCoordinateSequence cs = coordinates.get(i);
Object value = buildTaggedCoordinateSequence(cs);
// client oddity: needs spaces before and after the map-span elements to work
if (value instanceof String) {
if (i > 0) {
value = " " + value;
} else if (i < last - 1) {
value = value + " ";
}
}
result.add(value);
}

return result;
}

private Object buildTaggedCoordinateSequence(TaggedPolygon.TaggedCoordinateSequence cs) {
if (cs.isVisible()) {
return buildCoordinates(cs.getCoordinates());
} else {
return new Span(
"bbox",
buildCoordinates(
new CoordinateArraySequence(
cs.getCoordinates().toArray(n -> new Coordinate[n])),
null));
}
}

/**
* @param cs a JTS CoordinateSequence
* @param coordList a list of coordinate strings to add to
Expand All @@ -322,6 +397,35 @@ private List<String> buildCoordinates(CoordinateSequence cs, List<String> coordL
}
return coordList;
}

/**
* Builds a space separated representation of the coordinate sequence
*
* @param cs a JTS CoordinateSequence
* @return
*/
private String buildCoordinates(CoordinateSequence cs) {
StringJoiner joiner = new StringJoiner(" ");
for (int i = 0; i < cs.size(); i++) {
joiner.add(this.formatter.format(cs.getX(i)) + " " + this.formatter.format(cs.getY(i)));
}
return joiner.toString();
}

/**
* Builds a space separated representation of the list of coordinates
*
* @param cs a Coordinate list
* @return
*/
private String buildCoordinates(List<Coordinate> cs) {
StringJoiner joiner = new StringJoiner(" ");
for (Coordinate c : cs) {
joiner.add(this.formatter.format(c.getX()) + " " + this.formatter.format(c.getY()));
}
return joiner.toString();
}

/** @param numDecimals */
public void setNumDecimals(int numDecimals) {
// make a copy of relevant object state
Expand All @@ -333,10 +437,12 @@ public void setNumDecimals(int numDecimals) {
this.formatter.setForcedDecimal(fd);
this.formatter.setPadWithZeros(pad);
}

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

/** @param padWithZeros */
public void setPadWithZeros(boolean padWithZeros) {
this.formatter.setPadWithZeros(padWithZeros);
Expand Down
Loading

0 comments on commit 8fae5a5

Please sign in to comment.