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

VectorFeatureWriterHandler: fix axisOrder issue when CRS is North/East #976

Merged
merged 6 commits into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
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;
}

}