diff --git a/control-base/src/main/java/fi/nls/oskari/control/feature/FeatureWFSTRequestBuilder.java b/control-base/src/main/java/fi/nls/oskari/control/feature/FeatureWFSTRequestBuilder.java index 999c98f88f..c6ad28bcaf 100755 --- a/control-base/src/main/java/fi/nls/oskari/control/feature/FeatureWFSTRequestBuilder.java +++ b/control-base/src/main/java/fi/nls/oskari/control/feature/FeatureWFSTRequestBuilder.java @@ -1,10 +1,15 @@ package fi.nls.oskari.control.feature; import fi.nls.oskari.domain.map.Feature; - import fi.nls.oskari.log.LogFactory; import fi.nls.oskari.log.Logger; +import fi.nls.oskari.map.geometry.ProjectionHelper; import fi.nls.oskari.util.GML3Writer; + +import org.geotools.referencing.CRS; +import org.locationtech.jts.geom.Geometry; +import org.opengis.referencing.FactoryException; +import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.oskari.wfst.WFSTRequestBuilder; import javax.xml.stream.XMLStreamException; @@ -13,6 +18,7 @@ import java.util.Map; public class FeatureWFSTRequestBuilder extends WFSTRequestBuilder { + private final static Logger LOG = LogFactory.getLogger(FeatureWFSTRequestBuilder.class); public static void updateFeature(OutputStream out, Feature feature) @@ -62,7 +68,7 @@ public static void insertFeature(OutputStream out, Feature feature) if (feature.hasGeometry()) { xsw.writeStartElement(feature.getGMLGeometryProperty()); - GML3Writer.writeGeometry(xsw, feature.getGeometry()); + writeGeometry(xsw, feature.getGeometry()); xsw.writeEndElement(); } @@ -101,10 +107,25 @@ private static void writeGeometryProperty(XMLStreamWriter xsw, Feature feature) xsw.writeEndElement(); xsw.writeStartElement(WFS, "Value"); - GML3Writer.writeGeometry(xsw, feature.getGeometry()); + writeGeometry(xsw, feature.getGeometry()); xsw.writeEndElement(); xsw.writeEndElement(); } + private static void writeGeometry(XMLStreamWriter xsw, Geometry geometry) throws XMLStreamException { + boolean xyOrder = true; + if (geometry.getSRID() != 0) { + String srsName = GML3Writer.getSrsName(geometry.getSRID()); + try { + CoordinateReferenceSystem crs = CRS.decode(srsName); + xyOrder = !ProjectionHelper.isFirstAxisNorth(crs); + LOG.debug("srsName:", srsName, "xyOrder:", xyOrder); + } catch (FactoryException e) { + LOG.warn(e); + } + } + GML3Writer.writeGeometry(xsw, geometry, xyOrder); + } + } diff --git a/control-base/src/main/java/fi/nls/oskari/control/feature/VectorFeatureWriterHandler.java b/control-base/src/main/java/fi/nls/oskari/control/feature/VectorFeatureWriterHandler.java index 6b27eb516f..36e38e9a68 100644 --- a/control-base/src/main/java/fi/nls/oskari/control/feature/VectorFeatureWriterHandler.java +++ b/control-base/src/main/java/fi/nls/oskari/control/feature/VectorFeatureWriterHandler.java @@ -12,9 +12,12 @@ import fi.nls.oskari.util.JSONHelper; import fi.nls.oskari.util.ResponseHelper; import fi.nls.oskari.util.XmlHelper; + +import org.geotools.referencing.CRS; import org.json.JSONException; import org.json.JSONObject; import org.opengis.referencing.FactoryException; +import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; @@ -126,7 +129,7 @@ public void handlePut(ActionParameters params) throws ActionException { } } - private String createWFSTMessageForUpdate(Feature feature) + static String createWFSTMessageForUpdate(Feature feature) throws ActionException { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -136,7 +139,8 @@ private String createWFSTMessageForUpdate(Feature feature) throw new ActionException("Failed to create WFS-T request", e); } } - private String createWFSTMessageForInsert(Feature feature) + + static String createWFSTMessageForInsert(Feature feature) throws ActionException { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); diff --git a/control-base/src/test/java/fi/nls/oskari/control/feature/VectorFeatureWriterHandlerTest.java b/control-base/src/test/java/fi/nls/oskari/control/feature/VectorFeatureWriterHandlerTest.java new file mode 100644 index 0000000000..989b71311a --- /dev/null +++ b/control-base/src/test/java/fi/nls/oskari/control/feature/VectorFeatureWriterHandlerTest.java @@ -0,0 +1,97 @@ +package fi.nls.oskari.control.feature; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.HashMap; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.junit.Test; +import org.locationtech.jts.geom.CoordinateSequence; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Polygon; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +import fi.nls.oskari.domain.map.Feature; + +public class VectorFeatureWriterHandlerTest { + + @Test + public void testUpdateXYAxisOrder() throws Exception { + double[] pts = { + 473183.20423224,6680301.618281904, + 473257.20423224,6680411.618281904, + 473365.70423224,6680308.118281904, + 473251.20423224,6680215.618281904, + 473183.20423224,6680301.618281904 + }; + + Feature oskariFeature = new Feature(); + oskariFeature.setLayerName("foo"); + oskariFeature.setId("12345"); + oskariFeature.setProperties(new HashMap<>()); + oskariFeature.setGMLGeometryProperty("geometry"); + Geometry g = createPolygon(pts); + g.setSRID(3067); + oskariFeature.setGeometry(g); + + String wfsTransaction = VectorFeatureWriterHandler.createWFSTMessageForUpdate(oskariFeature); + double[] actual = readPosList(wfsTransaction); + + assertArrayEquals(pts, actual, 1e-10); + } + + @Test + public void testInsertYXAxisOrder() throws Exception { + double[] pts = { + 25473183.20423224,6680301.618281904, + 25473257.20423224,6680411.618281904, + 25473365.70423224,6680308.118281904, + 25473251.20423224,6680215.618281904, + 25473183.20423224,6680301.618281904 + }; + + Feature oskariFeature = new Feature(); + oskariFeature.setLayerName("foo"); + oskariFeature.setProperties(new HashMap<>()); + oskariFeature.setGMLGeometryProperty("geometry"); + Geometry g = createPolygon(pts); + g.setSRID(3879); + oskariFeature.setGeometry(g); + + String wfsTransaction = VectorFeatureWriterHandler.createWFSTMessageForInsert(oskariFeature); + double[] actual = readPosList(wfsTransaction); + for (int i = 0; i < pts.length / 2; i++) { + assertEquals(pts[i * 2 + 0], actual[i * 2 + 1], 1e-10); + assertEquals(pts[i * 2 + 1], actual[i * 2 + 0], 1e-10); + } + } + + private Polygon createPolygon(double[] pts) { + GeometryFactory gf = new GeometryFactory(); + CoordinateSequence seq = gf.getCoordinateSequenceFactory().create(pts.length / 2, 2); + for (int i = 0; i < pts.length / 2; i++) { + seq.setOrdinate(i, 0, pts[i * 2 + 0]); + seq.setOrdinate(i, 1, pts[i * 2 + 1]); + } + return gf.createPolygon(seq); + } + + private double[] readPosList(String wfsTransaction) throws SAXException, IOException, ParserConfigurationException { + byte[] wfsTransactionUTF8 = wfsTransaction.getBytes(StandardCharsets.UTF_8); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + Document doc = dbf.newDocumentBuilder().parse(new ByteArrayInputStream(wfsTransactionUTF8)); + String posList = doc.getElementsByTagNameNS("http://www.opengis.net/gml", "posList").item(0).getTextContent(); + return Arrays.stream(posList.split(" ")).mapToDouble(Double::parseDouble).toArray(); + } + +} diff --git a/service-base/src/main/java/fi/nls/oskari/util/GML3Writer.java b/service-base/src/main/java/fi/nls/oskari/util/GML3Writer.java index 665d16384a..2e5ad1c39a 100755 --- a/service-base/src/main/java/fi/nls/oskari/util/GML3Writer.java +++ b/service-base/src/main/java/fi/nls/oskari/util/GML3Writer.java @@ -19,137 +19,150 @@ public class GML3Writer { public static void writeGeometry(XMLStreamWriter xsw, Geometry geometry) throws XMLStreamException { + writeGeometry(xsw, geometry, true); + } + + public static void writeGeometry(XMLStreamWriter xsw, Geometry geometry, boolean xyOrder) + throws XMLStreamException { if (geometry instanceof Point) { - writePoint(xsw, (Point) geometry); + writePoint(xsw, (Point) geometry, xyOrder); } else if (geometry instanceof LineString) { - writeLineString(xsw, (LineString) geometry); + writeLineString(xsw, (LineString) geometry, xyOrder); } else if (geometry instanceof Polygon) { - writePolygon(xsw, (Polygon) geometry); + writePolygon(xsw, (Polygon) geometry, xyOrder); } else if (geometry instanceof MultiPoint) { - writeMultiPoint(xsw, (MultiPoint) geometry); + writeMultiPoint(xsw, (MultiPoint) geometry, xyOrder); } else if (geometry instanceof MultiLineString) { - writeMultiLineString(xsw, (MultiLineString) geometry); + writeMultiLineString(xsw, (MultiLineString) geometry, xyOrder); } else if (geometry instanceof MultiPolygon) { - writeMultiPolygon(xsw, (MultiPolygon) geometry); + writeMultiPolygon(xsw, (MultiPolygon) geometry, xyOrder); } else if (geometry instanceof GeometryCollection) { - writeMultiGeometry(xsw, (GeometryCollection) geometry); + writeMultiGeometry(xsw, (GeometryCollection) geometry, xyOrder); } else { throw new IllegalArgumentException(); } } - private static void writePoint(XMLStreamWriter xsw, Point geometry) + private static void writePoint(XMLStreamWriter xsw, Point geometry, boolean xyOrder) throws XMLStreamException { xsw.writeStartElement(GMLConstants.GML_NAMESPACE, GMLConstants.GML_POINT); writeSRID(xsw, geometry.getSRID()); - writePos(xsw, geometry.getCoordinate()); + writePos(xsw, geometry.getCoordinate(), xyOrder); xsw.writeEndElement(); } - private static void writeLineString(XMLStreamWriter xsw, LineString geometry) + private static void writeLineString(XMLStreamWriter xsw, LineString geometry, boolean xyOrder) throws XMLStreamException { xsw.writeStartElement(GMLConstants.GML_NAMESPACE, GMLConstants.GML_LINESTRING); writeSRID(xsw, geometry.getSRID()); - writePosList(xsw, geometry.getCoordinates()); + writePosList(xsw, geometry.getCoordinates(), xyOrder); xsw.writeEndElement(); } - private static void writePolygon(XMLStreamWriter xsw, Polygon geometry) + private static void writePolygon(XMLStreamWriter xsw, Polygon geometry, boolean xyOrder) throws XMLStreamException { xsw.writeStartElement(GMLConstants.GML_NAMESPACE, GMLConstants.GML_POLYGON); writeSRID(xsw, geometry.getSRID()); xsw.writeStartElement(GMLConstants.GML_NAMESPACE, "exterior"); - writeLinearRing(xsw, (LinearRing) geometry.getExteriorRing()); + writeLinearRing(xsw, (LinearRing) geometry.getExteriorRing(), xyOrder); xsw.writeEndElement(); for (int i = 0; i < geometry.getNumInteriorRing(); i++) { xsw.writeStartElement(GMLConstants.GML_NAMESPACE, "interior"); - writeLinearRing(xsw, (LinearRing) geometry.getInteriorRingN(i)); + writeLinearRing(xsw, (LinearRing) geometry.getInteriorRingN(i), xyOrder); xsw.writeEndElement(); } xsw.writeEndElement(); } - private static void writeMultiPoint(XMLStreamWriter xsw, MultiPoint geometry) + private static void writeMultiPoint(XMLStreamWriter xsw, MultiPoint geometry, boolean xyOrder) throws XMLStreamException { xsw.writeStartElement(GMLConstants.GML_NAMESPACE, GMLConstants.GML_MULTI_POINT); writeSRID(xsw, geometry.getSRID()); for (int i = 0; i < geometry.getNumGeometries(); i++) { xsw.writeStartElement(GMLConstants.GML_NAMESPACE, GMLConstants.GML_POINT_MEMBER); - writePoint(xsw, (Point) geometry.getGeometryN(i)); + writePoint(xsw, (Point) geometry.getGeometryN(i), xyOrder); xsw.writeEndElement(); } xsw.writeEndElement(); } - private static void writeMultiLineString(XMLStreamWriter xsw, MultiLineString geometry) + private static void writeMultiLineString(XMLStreamWriter xsw, MultiLineString geometry, boolean xyOrder) throws XMLStreamException { xsw.writeStartElement(GMLConstants.GML_NAMESPACE, GMLConstants.GML_MULTI_LINESTRING); writeSRID(xsw, geometry.getSRID()); for (int i = 0; i < geometry.getNumGeometries(); i++) { xsw.writeStartElement(GMLConstants.GML_NAMESPACE, GMLConstants.GML_LINESTRING_MEMBER); - writeLineString(xsw, (LineString) geometry.getGeometryN(i)); + writeLineString(xsw, (LineString) geometry.getGeometryN(i), xyOrder); xsw.writeEndElement(); } xsw.writeEndElement(); } - private static void writeMultiPolygon(XMLStreamWriter xsw, MultiPolygon geometry) + private static void writeMultiPolygon(XMLStreamWriter xsw, MultiPolygon geometry, boolean xyOrder) throws XMLStreamException { xsw.writeStartElement(GMLConstants.GML_NAMESPACE, GMLConstants.GML_MULTI_POLYGON); writeSRID(xsw, geometry.getSRID()); for (int i = 0; i < geometry.getNumGeometries(); i++) { xsw.writeStartElement(GMLConstants.GML_NAMESPACE, GMLConstants.GML_POLYGON_MEMBER); - writePolygon(xsw, (Polygon) geometry.getGeometryN(i)); + writePolygon(xsw, (Polygon) geometry.getGeometryN(i), xyOrder); xsw.writeEndElement(); } xsw.writeEndElement(); } - private static void writeMultiGeometry(XMLStreamWriter xsw, GeometryCollection geometry) + private static void writeMultiGeometry(XMLStreamWriter xsw, GeometryCollection geometry, boolean xyOrder) throws XMLStreamException { xsw.writeStartElement(GMLConstants.GML_NAMESPACE, GMLConstants.GML_MULTI_GEOMETRY); writeSRID(xsw, geometry.getSRID()); for (int i = 0; i < geometry.getNumGeometries(); i++) { xsw.writeStartElement(GMLConstants.GML_NAMESPACE, GMLConstants.GML_GEOMETRY_MEMBER); - writeGeometry(xsw, geometry.getGeometryN(i)); + writeGeometry(xsw, geometry.getGeometryN(i), xyOrder); xsw.writeEndElement(); } xsw.writeEndElement(); } - private static void writePos(XMLStreamWriter xsw, Coordinate coordinate) + private static void writePos(XMLStreamWriter xsw, Coordinate coordinate, boolean xyOrder) throws XMLStreamException { xsw.writeStartElement(GMLConstants.GML_NAMESPACE, "pos"); xsw.writeAttribute("srsDimension", "2"); - String pos = coordinate.x + " " + coordinate.y; + String pos = xyOrder ? coordinate.x + " " + coordinate.y : coordinate.y + " " + coordinate.x; xsw.writeCharacters(pos); xsw.writeEndElement(); } - private static void writePosList(XMLStreamWriter xsw, Coordinate[] coordinates) + private static void writePosList(XMLStreamWriter xsw, Coordinate[] coordinates, boolean xyOrder) throws XMLStreamException { xsw.writeStartElement(GMLConstants.GML_NAMESPACE, "posList"); xsw.writeAttribute("srsDimension", "2"); StringBuilder sb = new StringBuilder(); for (Coordinate coordinate : coordinates) { - sb.append(' ').append(coordinate.x).append(' ').append(coordinate.y); + if (xyOrder) { + sb.append(' ').append(coordinate.x).append(' ').append(coordinate.y); + } else { + sb.append(' ').append(coordinate.y).append(' ').append(coordinate.x); + } } xsw.writeCharacters(sb.substring(1)); xsw.writeEndElement(); } - private static void writeLinearRing(XMLStreamWriter xsw, LinearRing linearRing) + private static void writeLinearRing(XMLStreamWriter xsw, LinearRing linearRing, boolean xyOrder) throws XMLStreamException { xsw.writeStartElement(GMLConstants.GML_NAMESPACE, GMLConstants.GML_LINEARRING); - writePosList(xsw, linearRing.getCoordinates()); + writePosList(xsw, linearRing.getCoordinates(), xyOrder); xsw.writeEndElement(); } private static void writeSRID(XMLStreamWriter xsw, int srid) throws XMLStreamException { if (srid != 0) { - xsw.writeAttribute("srsName", "http://www.opengis.net/def/crs/EPSG/0/" + srid); + xsw.writeAttribute("srsName", getSrsName(srid)); } } + public static String getSrsName(int srid) { + return "http://www.opengis.net/def/crs/EPSG/0/" + srid; + } + }