Skip to content

Commit

Permalink
[GEOS-11605]: MapML Supporting custom TCRS/gridsets
Browse files Browse the repository at this point in the history
  • Loading branch information
dromagnoli committed Nov 19, 2024
1 parent b509070 commit 1d794f1
Show file tree
Hide file tree
Showing 32 changed files with 1,489 additions and 224 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 34 additions & 1 deletion doc/en/user/source/extensions/mapml/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,39 @@ If the ``Represent multi-layer requests as multiple elements`` is checked (and t

.. figure:: images/mapml_wms_multi_extent.png

TiledCRS
--------
MapML supports 4 built-in TiledCRS:

- MapML:WGS84 (or EPSG:4326)
- MapML:OSMTILE (or EPSG:3857)
- MapML:CBMTILE (or EPSG:3978)
- MapML:APSTILE (or EPSG:5936)

In addition, is it possible to configure custom TiledCRS based on the available WMTS GridSets.
A new MapML TCRS Settings menu is available in the GeoServer UI on the Settings section:

.. figure:: images/mapml_tcrs_menu.png


It provides a selector containing available GridSets. Administrator can select GridSets from the left list that will be converted to TiledCRSs.


.. figure:: images/mapml_tcrs_selector.png


Notes:

- Gridsets containing ":" character in the name won't be listed
- Gridsets with non-numeric levels or without a common prefix won't be listed


For example, the UTM14WGS84Quad specified in the above selector has the following definition, which only contains numeric level names.

.. figure:: images/mapml_utm_gridset.png



Styles
------

Expand Down Expand Up @@ -262,7 +295,7 @@ MapML resources will be available for any published WMS layers by making a GetMa

**SRS/CRS**

Note that the WMS SRS or CRS must be one of the projections supported by MapML:
Note that the WMS SRS or CRS must be one of the built-in projections supported by MapML or one of the TCRS configured through the dedicated section. Built-in MapML CRS are:

- MapML:WGS84 (or EPSG:4326)
- MapML:OSMTILE (or EPSG:3857)
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,13 @@
import org.geoserver.mapml.xml.Link;
import org.geoserver.mapml.xml.Mapml;
import org.geoserver.mapml.xml.Meta;
import org.geoserver.mapml.xml.ProjType;
import org.geoserver.mapml.xml.RelType;
import org.geoserver.ows.Request;
import org.geoserver.ows.URLMangler;
import org.geoserver.ows.util.ResponseUtils;
import org.geoserver.platform.ServiceException;
import org.geoserver.wms.featureinfo.FeatureTemplate;
import org.geotools.api.feature.simple.SimpleFeature;
import org.geotools.api.referencing.FactoryException;
import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
import org.geotools.api.referencing.crs.GeodeticCRS;
import org.geotools.api.style.Style;
Expand Down Expand Up @@ -409,7 +407,7 @@ private static Set<Meta> deduceProjectionAndExtent(
if (requestCRS != null) {
responseCRS = requestCRS;
responseCRSCode = CRS.toSRS(requestCRS);
tcrs = TiledCRSConstants.lookupTCRS(responseCRSCode);
tcrs = TiledCRSConstants.lookupTCRSParams(responseCRSCode);
if (tcrs != null) {
projection.setContent(tcrs.getName());
crs = (responseCRS instanceof GeodeticCRS) ? "gcrs" : "pcrs";
Expand Down Expand Up @@ -453,7 +451,7 @@ private static String getExtent(
"top-left-easting=%1$.2f,top-left-northing=%2$.2f,bottom-right-easting=%3$.2f,bottom-right-northing=%4$.2f";
double minLong, minLat, maxLong, maxLat;
double minEasting, minNorthing, maxEasting, maxNorthing;
TiledCRSParams tcrs = TiledCRSConstants.lookupTCRS(responseCRSCode);
TiledCRSParams tcrs = TiledCRSConstants.lookupTCRSParams(responseCRSCode);
try {
if (responseCRS instanceof GeodeticCRS) {
re = r.getLatLonBoundingBox();
Expand Down Expand Up @@ -507,14 +505,10 @@ public static List<Link> alternateProjections(
ArrayList<Link> links = new ArrayList<>();
Set<String> projections = TiledCRSConstants.tiledCRSBySrsName.keySet();
projections.forEach(
(String p) -> {
(String proj) -> {
Link l = new Link();
TiledCRSParams projection = TiledCRSConstants.lookupTCRS(p);
try {
l.setProjection(ProjType.fromValue(projection.getName()));
} catch (FactoryException e) {
throw new ServiceException("Invalid TCRS name");
}
TiledCRSParams projection = TiledCRSConstants.lookupTCRSParams(proj);
l.setProjection(projection.getName());
l.setRel(RelType.ALTERNATE);
query.put("srsName", "MapML:" + projection.getName());
HashMap<String, String> kvp = new HashMap<>(query.size());
Expand All @@ -529,7 +523,6 @@ public static List<Link> alternateProjections(
base, path, kvp, URLMangler.URLType.SERVICE)));
links.add(l);
});

return links;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import javax.servlet.http.HttpServletRequest;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.config.GeoServer;
import org.geoserver.mapml.xml.ProjType;
import org.geoserver.mapml.tcrs.MapMLProjection;
import org.geoserver.ows.Dispatcher;
import org.geoserver.ows.Request;
import org.geoserver.ows.URLMangler;
Expand Down Expand Up @@ -83,7 +83,7 @@ protected void write(
ReferencedEnvelope projectedBbox = extractBbox(fc);
LayerInfo layerInfo = gs.getCatalog().getLayerByName(fc.getSchema().getTypeName());
CoordinateReferenceSystem crs = projectedBbox.getCoordinateReferenceSystem();
ProjType projType = parseProjType(request);
MapMLProjection projType = parseProjType(request);
double longitude;
double latitude;
ReferencedEnvelope geographicBox;
Expand Down Expand Up @@ -116,7 +116,7 @@ protected void write(
osw.flush();
}

private ProjType parseProjType(Request request) throws ServiceException {
private MapMLProjection parseProjType(Request request) throws ServiceException {
try {
Map<String, Object> rawKvp = request.getRawKvp();
String srs;
Expand All @@ -127,7 +127,7 @@ private ProjType parseProjType(Request request) throws ServiceException {
} else {
srs = "EPSG:4326";
}
return ProjType.fromValue(srs.toUpperCase());
return new MapMLProjection(srs.toUpperCase());
} catch (IllegalArgumentException | FactoryException iae) {
// figure out the parameter name (version dependent) and the actual original
// string value for the srs/crs parameter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,27 @@

import static org.apache.commons.text.StringEscapeUtils.escapeHtml4;

import java.util.HashMap;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import org.geoserver.mapml.tcrs.Bounds;
import org.geoserver.mapml.tcrs.MapMLProjection;
import org.geoserver.mapml.tcrs.Point;
import org.geoserver.mapml.tcrs.TiledCRS;
import org.geoserver.mapml.xml.ProjType;
import org.geoserver.mapml.tcrs.TiledCRSParams;
import org.geoserver.ows.URLMangler;
import org.geoserver.ows.util.ResponseUtils;
import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.referencing.proj.PROJFormattable;
import org.geotools.referencing.proj.PROJFormatter;

/** Class delegated to build an HTML Document embedding a MapML Viewer. */
public class MapMLHTMLOutput {

public static final HashMap<String, TiledCRS> PREVIEW_TCRS_MAP = new HashMap<>();

static {
PREVIEW_TCRS_MAP.put("OSMTILE", new TiledCRS("OSMTILE"));
PREVIEW_TCRS_MAP.put("CBMTILE", new TiledCRS("CBMTILE"));
PREVIEW_TCRS_MAP.put("APSTILE", new TiledCRS("APSTILE"));
PREVIEW_TCRS_MAP.put("WGS84", new TiledCRS("WGS84"));
}

private String layerLabel;
private HttpServletRequest request;
private ProjType projType;
private MapMLProjection projType;
private String sourceUrL;
private int zoom = 0;
private Double latitude = 0.0;
Expand All @@ -54,7 +49,7 @@ private MapMLHTMLOutput(HTMLOutputBuilder builder) {
public static class HTMLOutputBuilder {
private String layerLabel;
private HttpServletRequest request;
private ProjType projType;
private MapMLProjection projType;
private String sourceUrL;
private ReferencedEnvelope projectedBbox;
private int zoom = 0;
Expand All @@ -72,7 +67,7 @@ public HTMLOutputBuilder setRequest(HttpServletRequest request) {
return this;
}

public HTMLOutputBuilder setProjType(ProjType projType) {
public HTMLOutputBuilder setProjType(MapMLProjection projType) {
this.projType = projType;
return this;
}
Expand Down Expand Up @@ -126,8 +121,9 @@ public String toHTML() {
"mapml-viewer:defined { max-width: 100%; width: 100%; height: 100%; border: none; vertical-align: middle }\n")
.append("mapml-viewer:not(:defined) > * { display: none; } n")
.append("map-layer { display: none; }\n")
.append("</style>\n")
.append("<noscript>\n")
.append("</style>\n");
appendProjectionScript(projType, sb);
sb.append("<noscript>\n")
.append("<style>\n")
.append("mapml-viewer:not(:defined) > :not(map-layer) { display: initial; }\n")
.append("</style>\n")
Expand All @@ -136,7 +132,7 @@ public String toHTML() {
.append("</head>\n")
.append("<body>\n")
.append("<mapml-viewer projection=\"")
.append(projType.value())
.append(projType.getTiledCRS().getParams().getName())
.append("\" ")
.append("zoom=\"")
.append(computeZoom(projType, projectedBbox))
Expand All @@ -158,14 +154,87 @@ public String toHTML() {
return sb.toString();
}

private void appendProjectionScript(MapMLProjection projType, StringBuilder sb) {
if (!projType.isBuiltIn()) {
sb.append("<script type=\"module\">\n")
.append("let customProjectionDefinition = `\n")
.append(buildDefinition(projType.getTiledCRS(), 10))
.append("let map = document.querySelector(\"mapml-viewer\");\n")
.append(
"let cProjection = map.defineCustomProjection(customProjectionDefinition);\n")
.append("map.projection = cProjection;\n")
.append("</script>");
}
}

private String buildDefinition(TiledCRS tiledCRS, int indentChars) {
TiledCRSParams params = tiledCRS.getParams();
int tileSize = params.getTILE_SIZE();
String name = params.getName();
Point origin = params.getOrigin();
String indent = " ".repeat(indentChars);
String originString = String.format("[%.8f, %.8f]", origin.getX(), origin.getY());

double[] resolutions = params.getResolutions();
StringBuilder resolutionsString = new StringBuilder("[");
for (int i = 0; i < resolutions.length; i++) {
resolutionsString.append(resolutions[i]);
if (i != resolutions.length - 1) {
resolutionsString.append(", ");
}
}
resolutionsString.append("]");

Bounds bounds = params.getBounds();
String boundsString =
String.format(
Locale.ENGLISH,
"[[%.8f, %.8f], [%.8f, %.8f]]",
bounds.getMin().getX(),
bounds.getMin().getY(),
bounds.getMax().getX(),
bounds.getMax().getY());

CoordinateReferenceSystem crs = tiledCRS.getCRS();
PROJFormatter formatter = new PROJFormatter();
String projString = formatter.toPROJ((PROJFormattable) crs);
StringBuilder sb =
new StringBuilder("{\n")
.append("\"projection\": \"")
.append(name)
.append("\",\n")
.append(indent)
.append("\"origin\": ")
.append(originString)
.append(",\n")
.append(indent)
.append("\"resolutions\": ")
.append(resolutionsString)
.append(",\n")
.append(indent)
.append("\"bounds\": ")
.append(boundsString)
.append(",\n")
.append(indent)
.append("\"tilesize\": ")
.append(tileSize)
.append(",\n")
.append(indent)
.append("\"proj4string\" : \"")
.append(projString)
.append("\"\n")
.append("}`;\n");
return sb.toString();
}

private String buildViewerPath(HttpServletRequest request) {
String base = ResponseUtils.baseURL(request);
return ResponseUtils.buildURL(
base, "/mapml/viewer/widget/mapml.js", null, URLMangler.URLType.RESOURCE);
}

private int computeZoom(ProjType projType, ReferencedEnvelope projectedBbox) {
TiledCRS tcrs = PREVIEW_TCRS_MAP.get(projType.value());
private int computeZoom(MapMLProjection projType, ReferencedEnvelope projectedBbox) {
TiledCRS tcrs = projType.getTiledCRS();
boolean flipAxis =
CRS.getAxisOrder(projectedBbox.getCoordinateReferenceSystem())
.equals(CRS.AxisOrder.NORTH_EAST);
Expand Down
Loading

0 comments on commit 1d794f1

Please sign in to comment.