From: Mathieu Baudier Date: Mon, 16 Oct 2023 07:11:33 +0000 (+0200) Subject: Introduce geometry to SVG X-Git-Tag: v2.3.17~6 X-Git-Url: http://git.argeo.org/?p=gpl%2Fargeo-suite.git;a=commitdiff_plain;h=2bdad474db365c49118b5a8e2d58c258d53b3d78 Introduce geometry to SVG --- diff --git a/org.argeo.app.geo/src/org/argeo/app/geo/GeoUtils.java b/org.argeo.app.geo/src/org/argeo/app/geo/GeoUtils.java index e565866..47096cc 100644 --- a/org.argeo.app.geo/src/org/argeo/app/geo/GeoUtils.java +++ b/org.argeo.app.geo/src/org/argeo/app/geo/GeoUtils.java @@ -47,7 +47,6 @@ import org.geotools.data.collection.ListFeatureCollection; import org.geotools.data.shapefile.ShapefileDataStore; import org.geotools.data.shapefile.ShapefileDataStoreFactory; import org.geotools.data.simple.SimpleFeatureCollection; -import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.factory.CommonFactoryFinder; import org.geotools.geometry.jts.JTS; import org.geotools.referencing.CRS; @@ -116,44 +115,45 @@ public class GeoUtils { } } - public static void exportToSvg(SimpleFeatureCollection features, Writer out, int width, int height) { + public static void exportToSvg(Geometry[] geometries, Writer out, int width, int height) { try { double minY = Double.POSITIVE_INFINITY; double maxY = Double.NEGATIVE_INFINITY; double minX = Double.POSITIVE_INFINITY; double maxX = Double.NEGATIVE_INFINITY; List shapes = new ArrayList<>(); - for (SimpleFeatureIterator it = features.features(); it.hasNext();) { - SimpleFeature feature = it.next(); + for (Geometry geometry : geometries) { StringBuffer sb = new StringBuffer(); sb.append(" maxX) - maxX = x; - double y = -coord.y; - if (y < minY) - minY = y; - if (y > maxY) - maxY = y; - sb.append(x + "," + y + " "); + if (geometry instanceof Polygon p) { + Point centroid = p.getCentroid(); + String code = "AUTO:42001," + centroid.getX() + "," + centroid.getY(); + CoordinateReferenceSystem auto = CRS.decode(code); + + MathTransform transform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, auto); + + Polygon projed = (Polygon) JTS.transform(p, transform); + + for (Coordinate coord : projed.getCoordinates()) { + double x = coord.x; + if (x < minX) + minX = x; + if (x > maxX) + maxX = x; + double y = -coord.y; + if (y < minY) + minY = y; + if (y > maxY) + maxY = y; + sb.append(x + "," + y + " "); + } + sb.append("\">"); + sb.append("\n"); + shapes.add(sb.toString()); + } else { + throw new IllegalArgumentException("Unsuppported geometry type " + geometry.getClass().getName()); } - sb.append("\">"); - sb.append("\n"); - shapes.add(sb.toString()); - } double viewportHeight = maxY - minY; double viewportWidth = maxX - minX; diff --git a/org.argeo.app.geo/src/org/argeo/app/geo/acr/GeoEntityUtils.java b/org.argeo.app.geo/src/org/argeo/app/geo/acr/GeoEntityUtils.java index dca6962..43f0a02 100644 --- a/org.argeo.app.geo/src/org/argeo/app/geo/acr/GeoEntityUtils.java +++ b/org.argeo.app.geo/src/org/argeo/app/geo/acr/GeoEntityUtils.java @@ -35,7 +35,6 @@ import jakarta.json.stream.JsonGenerator; /** Utilities around entity types related to geography. */ public class GeoEntityUtils { - public static final String PLACE_GEOM_JSON = "place.geom.json"; public static final String _GEOM_JSON = ".geom.json"; public static void putGeometry(Content c, QNamed name, Geometry geometry) { diff --git a/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/FopServlet.java b/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/FopServlet.java index 3344d75..c816198 100644 --- a/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/FopServlet.java +++ b/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/FopServlet.java @@ -40,15 +40,16 @@ import org.apache.xmlgraphics.io.ResourceResolver; import org.argeo.api.acr.Content; import org.argeo.api.acr.ContentRepository; import org.argeo.api.acr.ContentSession; +import org.argeo.api.acr.NamespaceUtils; import org.argeo.app.geo.GeoUtils; import org.argeo.app.geo.GpxUtils; +import org.argeo.app.geo.acr.GeoEntityUtils; import org.argeo.cms.acr.xml.XmlNormalizer; import org.argeo.cms.auth.RemoteAuthUtils; import org.argeo.cms.servlet.ServletHttpRequest; import org.argeo.cms.util.LangUtils; -import org.geotools.api.feature.simple.SimpleFeature; -import org.geotools.data.collection.ListFeatureCollection; -import org.geotools.data.simple.SimpleFeatureCollection; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.Polygon; import net.sf.saxon.BasicTransformerFactory; @@ -81,7 +82,7 @@ public class FopServlet extends HttpServlet { Content content = session.get(path); // dev only - final boolean DEV = false; + final boolean DEV = true; if (DEV) { try (InputStream in = xslUrl.openStream()) { Source xslSource = new StreamSource(in); @@ -109,21 +110,35 @@ public class FopServlet extends HttpServlet { return new StreamSource(in); } if (url.getScheme().equals("geo2svg")) { - String includePath = path + url.getPath(); - String geoExt = includePath.substring(includePath.lastIndexOf('.')); - Content geoContent = session.get(includePath); - if (".gpx".equals(geoExt)) { - try (InputStream in = geoContent.open(InputStream.class)) { - SimpleFeature field = GpxUtils.parseGpxToPolygon(in); - SimpleFeatureCollection features = new ListFeatureCollection(field.getType(), field); - try (StringWriter writer = new StringWriter()) { - GeoUtils.exportToSvg(features, writer, 100, 100); - StreamSource res = new StreamSource(new StringReader(writer.toString())); - return res; + int lastDot = url.getPath().lastIndexOf('.'); + Polygon polygon; + if (lastDot > 0) { + String includePath = path + url.getPath(); + Content geoContent = session.get(includePath); + String geoExt = includePath.substring(lastDot); + if (".gpx".equals(geoExt)) { + try (InputStream in = geoContent.open(InputStream.class)) { + polygon = GpxUtils.parseGpxTrackTo(in, Polygon.class); } + } else { + throw new UnsupportedOperationException(geoExt + " is not supported"); } } else { - throw new UnsupportedOperationException(geoExt + " is not supported"); + Content geoContent; + String attrName; + if (url.getPath().startsWith("/@")) { + geoContent = content; + attrName = url.getPath().substring(2);// remove /@ + } else { + throw new IllegalArgumentException("Only direct attributes are currently supported"); + } + polygon = GeoEntityUtils.getGeometry(geoContent, NamespaceUtils.parsePrefixedName(attrName), + Polygon.class); + } + try (StringWriter writer = new StringWriter()) { + GeoUtils.exportToSvg(new Geometry[] { polygon }, writer, 100, 100); + StreamSource res = new StreamSource(new StringReader(writer.toString())); + return res; } } } diff --git a/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/GeoToSvgServlet.java b/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/GeoToSvgServlet.java deleted file mode 100644 index 7ae9527..0000000 --- a/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/GeoToSvgServlet.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.argeo.app.servlet.publish; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; -import java.util.Map; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.argeo.api.acr.Content; -import org.argeo.api.acr.ContentRepository; -import org.argeo.api.acr.ContentSession; -import org.argeo.app.geo.GeoUtils; -import org.argeo.app.geo.GpxUtils; -import org.argeo.cms.auth.RemoteAuthUtils; -import org.argeo.cms.servlet.ServletHttpRequest; -import org.geotools.api.feature.simple.SimpleFeature; -import org.geotools.data.collection.ListFeatureCollection; -import org.geotools.data.simple.SimpleFeatureCollection; - -/** - * A servlet transforming an geographical data to SVG. - */ -public class GeoToSvgServlet extends HttpServlet { - private static final long serialVersionUID = -6346379324580671894L; - private ContentRepository contentRepository; - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - String servletPath = req.getServletPath(); - - servletPath = servletPath.substring(1, servletPath.lastIndexOf('.')); - servletPath = servletPath.substring(servletPath.indexOf('/'), servletPath.length()); - String path = URLDecoder.decode(servletPath, StandardCharsets.UTF_8); - String ext = servletPath.substring(path.lastIndexOf('.')); - - resp.setContentType("image/svg+xml"); - - ContentSession session = RemoteAuthUtils.doAs(() -> contentRepository.get(), new ServletHttpRequest(req)); - Content content = session.get(path); - if (".gpx".equals(ext)) { - try (InputStream in = content.open(InputStream.class)) { - SimpleFeature field = GpxUtils.parseGpxToPolygon(in); - - SimpleFeatureCollection features = new ListFeatureCollection(field.getType(), field); - GeoUtils.exportToSvg(features, resp.getWriter(), 100, 100); -// log.debug("SVG:\n" + writer.toString() + "\n"); - } - } - } - - public void start(Map properties) { - } - - public void stop(Map properties) { - - } - - public void setContentRepository(ContentRepository contentRepository) { - this.contentRepository = contentRepository; - } - -}