Skip to content
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

Mapml vector tiles - early test #363

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
prushforth marked this conversation as resolved.
Show resolved Hide resolved
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
Loading