Skip to content

Commit

Permalink
Merge pull request #976 from jampukka/feature/gml-axis-order
Browse files Browse the repository at this point in the history
VectorFeatureWriterHandler: fix axisOrder issue when CRS is North/East
  • Loading branch information
ZakarFin authored Aug 8, 2023
2 parents fcbd68c + 93dad11 commit c25632a
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}

}
71 changes: 42 additions & 29 deletions service-base/src/main/java/fi/nls/oskari/util/GML3Writer.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

}

0 comments on commit c25632a

Please sign in to comment.