OLINGO-1294 adding polygon support for multiple interior rings

This commit is contained in:
shawkins 2018-09-21 17:36:02 -04:00
parent 8cbe468c2a
commit ebdf0d3e3a
10 changed files with 147 additions and 69 deletions

View File

@ -113,8 +113,8 @@ class AtomGeoValueDeserializer {
private Polygon polygon(final XMLEventReader reader, final StartElement start,
final EdmPrimitiveTypeKind type, final SRID srid) throws XMLStreamException {
List<Point> extPoints = null;
List<Point> intPoints = null;
LineString extPoints = null;
List<LineString> intRings = new ArrayList<LineString>();
boolean foundEndProperty = false;
while (reader.hasNext() && !foundEndProperty) {
@ -122,10 +122,12 @@ class AtomGeoValueDeserializer {
if (event.isStartElement()) {
if (event.asStartElement().getName().equals(Constants.QNAME_POLYGON_EXTERIOR)) {
extPoints = points(reader, event.asStartElement(), type, null);
List<Point> points = points(reader, event.asStartElement(), type, null);
extPoints = new LineString(GeoUtils.getDimension(type), srid, points);
}
if (event.asStartElement().getName().equals(Constants.QNAME_POLYGON_INTERIOR)) {
intPoints = points(reader, event.asStartElement(), type, null);
List<Point> points = points(reader, event.asStartElement(), type, null);
intRings.add(new LineString(GeoUtils.getDimension(type), srid, points));
}
}
@ -134,7 +136,7 @@ class AtomGeoValueDeserializer {
}
}
return new Polygon(GeoUtils.getDimension(type), srid, intPoints, extPoints);
return new Polygon(GeoUtils.getDimension(type), srid, intRings, extPoints);
}
private MultiLineString multiLineString(final XMLEventReader reader, final StartElement start,

View File

@ -103,11 +103,11 @@ class AtomGeoValueSerializer {
writer.writeEndElement();
writer.writeEndElement();
}
if (!polygon.getInterior().isEmpty()) {
for (int i = 0; i < polygon.getNumberOfInteriorRings(); i++) {
writer.writeStartElement(Constants.PREFIX_GML, Constants.ELEM_POLYGON_INTERIOR, Constants.NS_GML);
writer.writeStartElement(Constants.PREFIX_GML, Constants.ELEM_POLYGON_LINEARRING, Constants.NS_GML);
points(writer, polygon.getInterior().iterator(), false);
points(writer, polygon.getInterior(i).iterator(), false);
writer.writeEndElement();
writer.writeEndElement();

View File

@ -127,19 +127,21 @@ class JsonGeoValueDeserializer {
}
}
List<Point> intPoints = null;
if (itor.hasNext()) {
List<LineString> intRings = new ArrayList<LineString>();
while (itor.hasNext()) {
final Iterator<JsonNode> intItor = itor.next().elements();
if (intItor.hasNext()) {
intPoints = new ArrayList<Point>();
List<Point> intPoints = new ArrayList<Point>();
while (intItor.hasNext()) {
final Iterator<JsonNode> mpItor = intItor.next().elements();
intPoints.add(point(mpItor, type, srid));
}
intRings.add(new LineString(GeoUtils.getDimension(type), srid, intPoints));
}
}
return new Polygon(GeoUtils.getDimension(type), srid, intPoints, extPoints);
LineString exterior = new LineString(GeoUtils.getDimension(type), srid, extPoints);
return new Polygon(GeoUtils.getDimension(type), srid, intRings, exterior);
}
private MultiPolygon multiPolygon(final Iterator<JsonNode> itor, final EdmPrimitiveTypeKind type, final SRID srid) {

View File

@ -84,14 +84,12 @@ class JsonGeoValueSerializer {
}
private void polygon(final JsonGenerator jgen, final Polygon polygon) throws IOException {
if (!polygon.getExterior().isEmpty()) {
jgen.writeStartArray();
lineString(jgen, polygon.getExterior());
jgen.writeEndArray();
for (int i = 0; i < polygon.getNumberOfInteriorRings(); i++) {
jgen.writeStartArray();
lineString(jgen, polygon.getExterior());
jgen.writeEndArray();
}
if (!polygon.getInterior().isEmpty()) {
jgen.writeStartArray();
lineString(jgen, polygon.getInterior());
lineString(jgen, polygon.getInterior(i));
jgen.writeEndArray();
}
}

View File

@ -18,6 +18,7 @@
*/
package org.apache.olingo.commons.api.edm.geo;
import java.util.Arrays;
import java.util.List;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
@ -27,7 +28,7 @@ import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
*/
public class Polygon extends Geospatial {
final ComposedGeospatial<Point> interior;
final ComposedGeospatial<LineString> interiorRings;
final ComposedGeospatial<Point> exterior;
/**
@ -37,28 +38,78 @@ public class Polygon extends Geospatial {
* @param srid SRID values
* @param interior List of interior points
* @param exterior List of exterior point
* @deprecated
*/
public Polygon(final Dimension dimension, final SRID srid,
final List<Point> interior, final List<Point> exterior) {
super(dimension, Type.POLYGON, srid);
this.interior = new MultiPoint(dimension, srid, interior);
this.exterior = new MultiPoint(dimension, srid, exterior);
if (interior != null) {
LineString lineString = new LineString(dimension, srid, interior);
this.interiorRings = new MultiLineString(dimension, srid, Arrays.asList(lineString));
} else {
this.interiorRings = null;
}
this.exterior = new LineString(dimension, srid, exterior);
}
/**
* Creates a new polygon.
*
* @param dimension Dimension of the polygon
* @param srid SRID values
* @param interiors List of interior rings
* @param exterior Ring of exterior point
*/
public Polygon(final Dimension dimension, final SRID srid,
final List<LineString> interiors, LineString exterior) {
super(dimension, Type.POLYGON, srid);
if (interiors != null) {
this.interiorRings = new MultiLineString(dimension, srid, interiors);
} else {
this.interiorRings = null;
}
this.exterior = exterior;
}
/**
* Gets interior points.
*
* @return interior points.
* @deprecated
* @see #getInterior(int)
*/
public ComposedGeospatial<Point> getInterior() {
return interior;
if (interiorRings == null || interiorRings.geospatials.isEmpty()) {
return null;
}
return getInterior(0);
}
/**
* Get the number of interior rings
* @return number of interior rings
*/
public int getNumberOfInteriorRings() {
if (interiorRings == null) {
return 0;
}
return interiorRings.geospatials.size();
}
/**
* Gets the nth interior ring
* @param n
* @return the ring or an exception if no such ring exists
*/
public ComposedGeospatial<Point> getInterior(int n) {
return interiorRings.geospatials.get(n);
}
/**
* Gets exterior points.
*
* @return exterior points.I
* @return exterior points.
*/
public ComposedGeospatial<Point> getExterior() {
return exterior;
@ -83,7 +134,7 @@ public class Polygon extends Geospatial {
final Polygon polygon = (Polygon) o;
return dimension == polygon.dimension
&& (srid == null ? polygon.srid == null : srid.equals(polygon.srid))
&& (interior == null ? polygon.interior == null : interior.equals(polygon.interior))
&& (interiorRings == null ? polygon.interiorRings == null : interiorRings.equals(polygon.interiorRings))
&& (exterior == null ? polygon.exterior == null : exterior.equals(polygon.exterior));
}
@ -91,7 +142,7 @@ public class Polygon extends Geospatial {
public int hashCode() {
int result = dimension == null ? 0 : dimension.hashCode();
result = 31 * result + (srid == null ? 0 : srid.hashCode());
result = 31 * result + (interior == null ? 0 : interior.hashCode());
result = 31 * result + (interiorRings == null ? 0 : interiorRings.hashCode());
result = 31 * result + (exterior == null ? 0 : exterior.hashCode());
return result;
}

View File

@ -26,6 +26,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
import org.apache.olingo.commons.api.edm.geo.ComposedGeospatial;
import org.apache.olingo.commons.api.edm.geo.Geospatial;
import org.apache.olingo.commons.api.edm.geo.Geospatial.Dimension;
import org.apache.olingo.commons.api.edm.geo.Geospatial.Type;
@ -176,16 +177,20 @@ public abstract class AbstractGeospatialType<T extends Geospatial> extends Singl
final String[] first = polygon.split("\\),\\(");
final List<Point> interior = new ArrayList<Point>();
for (final String pointCoo : split(first[0].substring(1, first[0].length()), ',')) {
interior.add(newPoint(null, pointCoo, isNullable, maxLength, precision, scale, isUnicode));
final List<LineString> interiorRings = new ArrayList<LineString>();
for (int i = 0; i < first.length -1; i++) {
List<Point> interior = new ArrayList<Point>();
for (final String pointCoo : split(first[i].substring(i==0?1:0, first[i].length()), ',')) {
interior.add(newPoint(null, pointCoo, isNullable, maxLength, precision, scale, isUnicode));
}
interiorRings.add(new LineString(dimension, srid, interior));
}
final List<Point> exterior = new ArrayList<Point>();
for (final String pointCoo : split(first[1].substring(0, first[1].length() - 1), ',')) {
for (final String pointCoo : split(first[first.length -1].substring(0, first[first.length -1].length() - 1), ',')) {
exterior.add(newPoint(null, pointCoo, isNullable, maxLength, precision, scale, isUnicode));
}
return new Polygon(dimension, srid, interior, exterior);
return new Polygon(dimension, srid, interiorRings, new LineString(dimension, srid, exterior));
}
protected Polygon stringToPolygon(final String value, final Boolean isNullable, final Integer maxLength,
@ -343,21 +348,17 @@ public abstract class AbstractGeospatialType<T extends Geospatial> extends Singl
return result.append(")'").toString();
}
private String lineString(final LineString lineString, final Boolean isNullable,
final Integer maxLength, final Integer precision, final Integer scale, final Boolean isUnicode)
throws EdmPrimitiveTypeException {
final StringBuilder result = new StringBuilder();
for (final Iterator<Point> itor = lineString.iterator(); itor.hasNext();) {
private StringBuilder appendPoints(final ComposedGeospatial<Point> points, final Boolean isNullable,
final Integer maxLength, final Integer precision, final Integer scale, final Boolean isUnicode,
final StringBuilder result) throws EdmPrimitiveTypeException {
for (final Iterator<Point> itor = points.iterator(); itor.hasNext();) {
result.append(point(itor.next(), isNullable, maxLength, precision, scale, isUnicode));
if (itor.hasNext()) {
result.append(',');
}
}
return result.toString();
}
return result;
}
protected String toString(final LineString lineString, final Boolean isNullable, final Integer maxLength,
final Integer precision, final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException {
@ -366,10 +367,10 @@ public abstract class AbstractGeospatialType<T extends Geospatial> extends Singl
throw new EdmPrimitiveTypeException("The value '" + lineString + "' is not valid.");
}
return toStringBuilder(lineString.getSrid()).
StringBuilder builder = toStringBuilder(lineString.getSrid()).
append(reference.getSimpleName()).
append('(').
append(lineString(lineString, isNullable, maxLength, precision, scale, isUnicode)).
append('(');
return appendPoints(lineString, isNullable, maxLength, precision, scale, isUnicode, builder).
append(")'").toString();
}
@ -385,8 +386,8 @@ public abstract class AbstractGeospatialType<T extends Geospatial> extends Singl
append('(');
for (final Iterator<LineString> itor = multiLineString.iterator(); itor.hasNext();) {
result.append('(').
append(lineString(itor.next(), isNullable, maxLength, precision, scale, isUnicode)).
result.append('(');
appendPoints(itor.next(), isNullable, maxLength, precision, scale, isUnicode, result).
append(')');
if (itor.hasNext()) {
result.append(',');
@ -402,20 +403,14 @@ public abstract class AbstractGeospatialType<T extends Geospatial> extends Singl
final StringBuilder result = new StringBuilder();
for (int i = 0; i < polygon.getNumberOfInteriorRings(); i++) {
result.append('(');
appendPoints(polygon.getInterior(i), isNullable, maxLength, precision, scale, isUnicode, result);
result.append("),");
}
result.append('(');
for (final Iterator<Point> itor = polygon.getInterior().iterator(); itor.hasNext();) {
result.append(point(itor.next(), isNullable, maxLength, precision, scale, isUnicode));
if (itor.hasNext()) {
result.append(',');
}
}
result.append("),(");
for (final Iterator<Point> itor = polygon.getExterior().iterator(); itor.hasNext();) {
result.append(point(itor.next(), isNullable, maxLength, precision, scale, isUnicode));
if (itor.hasNext()) {
result.append(',');
}
}
appendPoints(polygon.getExterior(), isNullable, maxLength, precision, scale, isUnicode, result);
return result.append(')').toString();
}
@ -490,14 +485,14 @@ public abstract class AbstractGeospatialType<T extends Geospatial> extends Singl
case GeographyLineString:
case GeometryLineString:
result.append(lineString((LineString) item, isNullable, maxLength, precision, scale, isUnicode));
appendPoints((LineString) item, isNullable, maxLength, precision, scale, isUnicode, result);
break;
case GeographyMultiLineString:
case GeometryMultiLineString:
for (final Iterator<LineString> itor = ((MultiLineString) item).iterator(); itor.hasNext();) {
result.append('(').
append(lineString(itor.next(), isNullable, maxLength, precision, scale, isUnicode)).
result.append('(');
appendPoints(itor.next(), isNullable, maxLength, precision, scale, isUnicode, result).
append(')');
if (itor.hasNext()) {
result.append(',');

View File

@ -120,7 +120,7 @@ public class EdmGeoTest extends PrimitiveTypeBaseTest {
valueOfString(input, null, null, null, null, null, Polygon.class);
assertNotNull(polygon);
assertEquals("0", polygon.getSrid().toString());
Iterator<Point> itor = polygon.getInterior().iterator();
Iterator<Point> itor = polygon.getInterior(0).iterator();
assertEquals(1, itor.next().getX(), 0);
assertEquals(1, itor.next().getY(), 0);
itor = polygon.getExterior().iterator();
@ -130,6 +130,31 @@ public class EdmGeoTest extends PrimitiveTypeBaseTest {
assertEquals(input, EdmGeographyPolygon.getInstance().valueToString(polygon, null, null, null, null, null));
}
@Test
public void polygonMultipleHoles() throws EdmPrimitiveTypeException {
final String input = "geography'SRID=4326;Polygon((1.0 1.0,1.0 1.0),(2.0 2.0,2.0 2.0)"
+ ",(1.0 1.0,2.0 2.0,3.0 3.0,1.0 1.0))'";
expectContentErrorInValueOfString(EdmGeometryPolygon.getInstance(), input);
final Polygon polygon = EdmGeographyPolygon.getInstance().
valueOfString(input, null, null, null, null, null, Polygon.class);
assertNotNull(polygon);
assertEquals("4326", polygon.getSrid().toString());
Iterator<Point> itor = polygon.getInterior(0).iterator();
assertEquals(1, itor.next().getX(), 0);
assertEquals(1, itor.next().getY(), 0);
itor = polygon.getInterior(1).iterator();
assertEquals(2, itor.next().getX(), 0);
assertEquals(2, itor.next().getY(), 0);
itor = polygon.getExterior().iterator();
itor.next();
assertEquals(2, itor.next().getX(), 0);
assertEquals(3, itor.next().getY(), 0);
assertEquals(input, EdmGeographyPolygon.getInstance().valueToString(polygon, null, null, null, null, null));
}
@Test
public void multiPolygon() throws EdmPrimitiveTypeException {
@ -145,8 +170,8 @@ public class EdmGeoTest extends PrimitiveTypeBaseTest {
assertNotNull(multiPolygon);
assertEquals("0", multiPolygon.getSrid().toString());
final Iterator<Polygon> itor = multiPolygon.iterator();
assertEquals(1, itor.next().getInterior().iterator().next().getX(), 0);
assertEquals(1, itor.next().getInterior().iterator().next().getX(), 0);
assertEquals(1, itor.next().getInterior(0).iterator().next().getX(), 0);
assertEquals(1, itor.next().getInterior(0).iterator().next().getX(), 0);
assertEquals(input, EdmGeometryMultiPolygon.getInstance().
valueToString(multiPolygon, null, null, null, null, null));

View File

@ -1060,9 +1060,9 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
json.writeStartArray();
writeGeoPoints(json, polygon.getExterior());
json.writeEndArray();
if (!polygon.getInterior().isEmpty()) {
for (int i = 0; i < polygon.getNumberOfInteriorRings(); i++) {
json.writeStartArray();
writeGeoPoints(json, polygon.getInterior());
writeGeoPoints(json, polygon.getInterior(i));
json.writeEndArray();
}
}

View File

@ -603,6 +603,11 @@ public class UriTokenizerTest {
public void geoPolygon() {
assertTrue(new UriTokenizer("geography'SRID=4326;Polygon((0 0,1 0,0 1,0 0))'").next(TokenKind.GeographyPolygon));
assertTrue(new UriTokenizer("geometry'SRID=0;Polygon((0 0,1 0,0 1,0 0))'").next(TokenKind.GeometryPolygon));
assertTrue(new UriTokenizer("geometry'SRID=0;Polygon((1 1,2 1,2 2,1 2,1 1),(0 0,4 0,4 4,0 4,0 0))'")
.next(TokenKind.GeometryPolygon));
assertTrue(new UriTokenizer(
"geometry'SRID=0;Polygon((0 0,1 1,2 2,0 0),(1 1,2 1,2 2,1 2,1 1),(0 0,4 0,4 4,0 4,0 0))'")
.next(TokenKind.GeometryPolygon));
wrongToken(TokenKind.GeometryPolygon,
"geometry'SRID=0;Polygon((0 0,4 0,4 4,0 4,0 0),(1 1,2 1,2 2,1 2,1 1))'",
'x');

View File

@ -1036,13 +1036,13 @@ public class ODataJsonDeserializerEntityTest extends AbstractODataDeserializerTe
Polygon polygon = (Polygon) entity.getProperties().get(0).getValue();
assertEquals(Geospatial.Dimension.GEOMETRY, polygon.getDimension());
assertEquals(0, polygon.getExterior().iterator().next().getX(), 0);
assertEquals(1, polygon.getInterior().iterator().next().getY(), 0);
assertEquals(1, polygon.getInterior(0).iterator().next().getY(), 0);
entity = deserialize("{\"" + entityType.getPropertyNames().get(0) + "\":{"
+ "\"type\":\"Polygon\",\"coordinates\":[[[0,0],[3,0],[3,3],[0,3],[0,0]]]}}",
entityType);
polygon = (Polygon) entity.getProperties().get(0).getValue();
assertTrue(polygon.getInterior().isEmpty());
assertEquals(0, polygon.getNumberOfInteriorRings());
expectException("{\"" + entityType.getPropertyNames().get(0) + "\":{"
+ "\"type\":\"Polygon\",\"coordinates\":{\"ext\":[[0,0],[3,0],[0,3],[0,0]]}}}", entityType,
@ -1070,7 +1070,7 @@ public class ODataJsonDeserializerEntityTest extends AbstractODataDeserializerTe
+ "[[[0.0,0.0],[30.0,0.0],[0.0,30.0],[0.0,0.0]]]]}}",
entityType);
final MultiPolygon multiPolygon = (MultiPolygon) entity.getProperties().get(0).getValue();
assertEquals(1, multiPolygon.iterator().next().getInterior().iterator().next().getX(), 0);
assertEquals(1, multiPolygon.iterator().next().getInterior(0).iterator().next().getX(), 0);
expectException("{\"" + entityType.getPropertyNames().get(0) + "\":{"
+ "\"type\":\"MultiPolygon\",\"coordinates\":[{\"first\":[[[0,0],[3,0],[3,3],[0,3],[0,0]]]}]}}", entityType,