Skip to content
Snippets Groups Projects
Commit 327bc272 authored by Neelesh Thakur's avatar Neelesh Thakur
Browse files

add support for featurecollection

parent bcf6f97e
No related branches found
No related tags found
No related merge requests found
This commit is part of merge request !124. Comments created here will be created in the context of that merge request.
Showing
with 1039 additions and 526 deletions
package org.opengroup.osdu.indexer.model.geojson;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
@Data
public class Feature extends GeoJsonObject {
@JsonInclude()
private Map<String, Object> properties = new HashMap<String, Object>();
@JsonInclude()
private GeoJsonObject geometry;
private String id;
public void setProperty(String key, Object value) {
properties.put(key, value);
}
@SuppressWarnings("unchecked")
public <T> T getProperty(String key) {
return (T) properties.get(key);
}
}
package org.opengroup.osdu.indexer.model.geojson;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;
import org.opengroup.osdu.indexer.model.geojson.jackson.FeatureCollectionSerializer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
@Data
@JsonSerialize(using = FeatureCollectionSerializer.class)
public class FeatureCollection extends GeoJsonObject implements Iterable<Feature> {
private List<Feature> features = new ArrayList<Feature>();
public FeatureCollection add(Feature feature) {
features.add(feature);
return this;
}
public void addAll(Collection<Feature> features) {
this.features.addAll(features);
}
@Override
public Iterator<Feature> iterator() {
return features.iterator();
}
}
package org.opengroup.osdu.indexer.model.geojson;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import lombok.Data;
@Data
@JsonTypeInfo(property = "type", use = Id.NAME)
@JsonSubTypes({ @Type(Feature.class), @Type(Polygon.class), @Type(MultiPolygon.class), @Type(FeatureCollection.class),
@Type(Point.class), @Type(MultiPoint.class), @Type(MultiLineString.class), @Type(LineString.class),
@Type(GeometryCollection.class) })
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class GeoJsonObject implements Serializable {
}
package org.opengroup.osdu.indexer.model.geojson;
import java.util.ArrayList;
import java.util.List;
public abstract class Geometry<T> extends GeoJsonObject {
protected List<T> coordinates = new ArrayList<T>();
public Geometry() {
}
public Geometry(T... elements) {
for (T coordinate : elements) {
coordinates.add(coordinate);
}
}
public Geometry<T> add(T elements) {
coordinates.add(elements);
return this;
}
public List<T> getCoordinates() {
return coordinates;
}
public void setCoordinates(List<T> coordinates) {
this.coordinates = coordinates;
}
}
package org.opengroup.osdu.indexer.model.geojson;
import lombok.Data;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@Data
public class GeometryCollection extends GeoJsonObject implements Iterable<GeoJsonObject> {
private List<GeoJsonObject> geometries = new ArrayList<GeoJsonObject>();
@Override
public Iterator<GeoJsonObject> iterator() {
return geometries.iterator();
}
public GeometryCollection add(GeoJsonObject geometry) {
geometries.add(geometry);
return this;
}
}
package org.opengroup.osdu.indexer.model.geojson;
import lombok.NoArgsConstructor;
@NoArgsConstructor
public class LineString extends MultiPoint {
public LineString(Position... points) {
super(points);
}
}
package org.opengroup.osdu.indexer.model.geojson;
import lombok.NoArgsConstructor;
import java.util.List;
@NoArgsConstructor
public class MultiLineString extends Geometry<List<Position>> {
public MultiLineString(List<Position> line) {
add(line);
}
}
package org.opengroup.osdu.indexer.model.geojson;
import lombok.NoArgsConstructor;
@NoArgsConstructor
public class MultiPoint extends Geometry<Position> {
public MultiPoint(Position... points) {
super(points);
}
}
package org.opengroup.osdu.indexer.model.geojson;
import lombok.NoArgsConstructor;
import java.util.List;
@NoArgsConstructor
public class MultiPolygon extends Geometry<List<List<Position>>> {
public MultiPolygon(Polygon polygon) {
add(polygon);
}
public MultiPolygon add(Polygon polygon) {
coordinates.add(polygon.getCoordinates());
return this;
}
}
package org.opengroup.osdu.indexer.model.geojson;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Point extends GeoJsonObject {
private Position coordinates;
public Point(double longitude, double latitude) {
coordinates = new Position(longitude, latitude);
}
public Point(double longitude, double latitude, double altitude) {
coordinates = new Position(longitude, latitude, altitude);
}
}
package org.opengroup.osdu.indexer.model.geojson;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor;
import java.util.Arrays;
import java.util.List;
@NoArgsConstructor
public class Polygon extends Geometry<List<Position>> {
public Polygon(List<Position> polygon) {
add(polygon);
}
public Polygon(Position... polygon) {
add(Arrays.asList(polygon));
}
public void setExteriorRing(List<Position> points) {
if (coordinates.isEmpty()) {
coordinates.add(0, points);
} else {
coordinates.set(0, points);
}
}
@JsonIgnore
public List<Position> getExteriorRing() {
assertExteriorRing();
return coordinates.get(0);
}
@JsonIgnore
public List<List<Position>> getInteriorRings() {
assertExteriorRing();
return coordinates.subList(1, coordinates.size());
}
public List<Position> getInteriorRing(int index) {
assertExteriorRing();
return coordinates.get(1 + index);
}
public void addInteriorRing(List<Position> points) {
assertExteriorRing();
coordinates.add(points);
}
public void addInteriorRing(Position... points) {
assertExteriorRing();
coordinates.add(Arrays.asList(points));
}
private void assertExteriorRing() {
if (coordinates.isEmpty())
throw new RuntimeException("No exterior ring defined");
}
}
package org.opengroup.osdu.indexer.model.geojson;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.opengroup.osdu.indexer.model.geojson.jackson.PositionDeserializer;
import org.opengroup.osdu.indexer.model.geojson.jackson.PositionSerializer;
import java.io.Serializable;
@Getter
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
@AllArgsConstructor
@JsonDeserialize(using = PositionDeserializer.class)
@JsonSerialize(using = PositionSerializer.class)
public class Position implements Serializable {
private double longitude;
private double latitude;
@JsonIgnore
private double altitude = Double.NaN;
public Position(double longitude, double latitude) {
this.setLongitude(longitude);
this.setLatitude(latitude);
}
public void setLongitude(double longitude) {
if (Double.isNaN(longitude))
throw new IllegalArgumentException("latitude must be number");
if (longitude > 180 || longitude < -180)
throw new IllegalArgumentException("'longitude' value is out of the range [-180, 180]");
this.longitude = longitude;
}
public void setLatitude(double latitude) {
if (Double.isNaN(latitude))
throw new IllegalArgumentException("latitude must be number");
if (latitude > 90 || latitude < -90)
throw new IllegalArgumentException("latitude value is out of the range [-90, 90]");
this.latitude = latitude;
}
public void setAltitude(double altitude) {
this.altitude = altitude;
}
public boolean hasAltitude() {
return !Double.isNaN(altitude);
}
}
package org.opengroup.osdu.indexer.model.geojson.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import lombok.Data;
import org.opengroup.osdu.indexer.model.geojson.*;
import java.io.IOException;
@Data
public class FeatureCollectionSerializer extends JsonSerializer<FeatureCollection> {
@Override
public void serialize(FeatureCollection value, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("type", "geometrycollection");
jsonGenerator.writeArrayFieldStart("geometries");
for (Feature feature : value.getFeatures()) {
jsonGenerator.writeStartObject();
if (feature.getGeometry() instanceof GeometryCollection) {
GeometryCollection geometryCollection = (GeometryCollection) feature.getGeometry();
for (GeoJsonObject shape : geometryCollection.getGeometries()) {
serializeGeoShape(shape, jsonGenerator);
}
} else {
serializeGeoShape(feature.getGeometry(), jsonGenerator);
}
jsonGenerator.writeEndObject();
}
jsonGenerator.writeEndArray();
jsonGenerator.writeEndObject();
}
@Override
public void serializeWithType(FeatureCollection value, JsonGenerator jsonGenerator, SerializerProvider provider, TypeSerializer typeSerializer)
throws IOException, JsonProcessingException {
serialize(value, jsonGenerator, provider);
}
private void serializeGeoShape(GeoJsonObject geoJsonObject, JsonGenerator jsonGenerator) throws IOException {
if (geoJsonObject instanceof Point) {
jsonGenerator.writeStringField("type", "point");
jsonGenerator.writeObjectField("coordinates", ((Point) geoJsonObject).getCoordinates());
} else if (geoJsonObject instanceof LineString) {
jsonGenerator.writeStringField("type", "linestring");
jsonGenerator.writeObjectField("coordinates", ((LineString) geoJsonObject).getCoordinates());
} else if (geoJsonObject instanceof Polygon) {
jsonGenerator.writeStringField("type", "polygon");
jsonGenerator.writeObjectField("coordinates", ((Polygon) geoJsonObject).getCoordinates());
} else if (geoJsonObject instanceof MultiPoint) {
jsonGenerator.writeStringField("type", "multipoint");
jsonGenerator.writeObjectField("coordinates", ((MultiPoint) geoJsonObject).getCoordinates());
} else if (geoJsonObject instanceof MultiLineString) {
jsonGenerator.writeStringField("type", "multilinestring");
jsonGenerator.writeObjectField("coordinates", ((MultiLineString) geoJsonObject).getCoordinates());
} else if (geoJsonObject instanceof MultiPolygon) {
jsonGenerator.writeStringField("type", "multipolygon");
jsonGenerator.writeObjectField("coordinates", ((MultiPolygon) geoJsonObject).getCoordinates());
}
}
}
package org.opengroup.osdu.indexer.model.geojson.jackson;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import org.opengroup.osdu.indexer.model.geojson.Position;
import java.io.IOException;
public class PositionDeserializer extends JsonDeserializer<Position> {
@Override
public Position deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
if (jsonParser.isExpectedStartArrayToken()) {
return deserializeArray(jsonParser, context);
}
throw context.mappingException(Position.class);
}
protected Position deserializeArray(JsonParser jsonParser, DeserializationContext context) throws IOException {
Position node = new Position();
node.setLongitude(extractDouble(jsonParser, context, false));
node.setLatitude(extractDouble(jsonParser, context, false));
node.setAltitude(extractDouble(jsonParser, context, true));
return node;
}
private double extractDouble(JsonParser jsonParser, DeserializationContext context, boolean optional) throws IOException {
JsonToken token = jsonParser.nextToken();
if (token == null) {
if (optional)
return Double.NaN;
else
throw context.mappingException("Unexpected end-of-input when binding data into Position");
} else {
switch (token) {
case END_ARRAY:
if (optional)
return Double.NaN;
else
throw context.mappingException("Unexpected end-of-input when binding data into Position");
case VALUE_NUMBER_FLOAT:
return jsonParser.getDoubleValue();
case VALUE_NUMBER_INT:
return jsonParser.getLongValue();
default:
throw context.mappingException(
"Unexpected token (" + token.name() + ") when binding data into Position");
}
}
}
}
package org.opengroup.osdu.indexer.model.geojson.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.opengroup.osdu.indexer.model.geojson.Position;
import java.io.IOException;
public class PositionSerializer extends JsonSerializer<Position> {
@Override
public void serialize(Position value, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException {
jsonGenerator.writeStartArray();
jsonGenerator.writeNumber(value.getLongitude());
jsonGenerator.writeNumber(value.getLatitude());
if (value.hasAltitude()) {
jsonGenerator.writeNumber(value.getAltitude());
}
jsonGenerator.writeEndArray();
}
}
......@@ -25,6 +25,7 @@ import org.opengroup.osdu.core.common.model.indexer.IndexSchema;
import org.opengroup.osdu.core.common.model.indexer.IndexingStatus;
import org.opengroup.osdu.core.common.model.indexer.JobStatus;
import org.opengroup.osdu.core.common.Constants;
import org.opengroup.osdu.indexer.model.geojson.FeatureCollection;
import org.opengroup.osdu.indexer.util.parser.BooleanParser;
import org.opengroup.osdu.indexer.util.parser.DateTimeParser;
import org.opengroup.osdu.indexer.util.parser.GeoShapeParser;
......@@ -185,7 +186,7 @@ public class AttributeParsingServiceImpl implements IAttributeParsingService {
dataMap.put(DATA_GEOJSON_TAG, geometry);
}
} catch (JsonSyntaxException | IllegalArgumentException e) {
String parsingError = String.format("geopoint parsing error: %s attribute: %s | value: %s", e.getMessage(), attributeName, attributeVal);
String parsingError = String.format("geo-point parsing error: %s attribute: %s | value: %s", e.getMessage(), attributeName, attributeVal);
jobStatus.addOrUpdateRecordStatus(recordId, IndexingStatus.WARN, HttpStatus.SC_BAD_REQUEST, parsingError, String.format("record-id: %s | %s", recordId, parsingError));
}
}
......@@ -199,9 +200,9 @@ public class AttributeParsingServiceImpl implements IAttributeParsingService {
if (geoJsonMap == null || geoJsonMap.isEmpty()) return;
this.geoShapeParser.parseGeoJson(geoJsonMap);
Map<String, Object> parsedShape = this.geoShapeParser.parseGeoJson(geoJsonMap);
dataMap.put(attributeName, geoJsonMap);
dataMap.put(attributeName, parsedShape);
} catch (JsonSyntaxException | IllegalArgumentException e) {
String parsingError = String.format("geo-json shape parsing error: %s attribute: %s", e.getMessage(), attributeName);
jobStatus.addOrUpdateRecordStatus(recordId, IndexingStatus.WARN, HttpStatus.SC_BAD_REQUEST, parsingError, String.format("record-id: %s | %s", recordId, parsingError));
......
......@@ -14,55 +14,36 @@
package org.opengroup.osdu.indexer.util.parser;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.elasticsearch.common.xcontent.DeprecationHandler;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.locationtech.spatial4j.exception.InvalidShapeException;
import org.locationtech.spatial4j.shape.Shape;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
import org.opengroup.osdu.core.common.search.Preconditions;
import org.opengroup.osdu.indexer.model.geojson.FeatureCollection;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
import java.io.IOException;
import java.util.Map;
@Component
@RequestScope
public class GeoShapeParser {
public String parseGeoJson(Map<String, Object> geoShapeObject) {
private ObjectMapper mapper = new ObjectMapper();
Preconditions.checkNotNull(geoShapeObject, "geoShapeObject cannot be null");
public Map<String, Object> parseGeoJson(Map<String, Object> objectMap) {
try {
// use elasticsearch's ShapeParser to validate shape
ShapeBuilder shapeBuilder = getShapeBuilderFromObject(geoShapeObject);
Shape shape = shapeBuilder.buildS4J();
if (shape == null) {
throw new IllegalArgumentException("unable to parse shape");
}
Preconditions.checkNotNull(objectMap, "geoShapeObject cannot be null");
if (objectMap.isEmpty()) throw new IllegalArgumentException("shape not included");
return shapeBuilder.toString().replaceAll("\\r", "").replaceAll("\\n", "");
} catch (ElasticsearchParseException | InvalidShapeException | IOException e) {
throw new IllegalArgumentException(e.getMessage(), e);
try {
FeatureCollection collection = mapper.readValue(mapper.writeValueAsString(objectMap), FeatureCollection.class);
return mapper.readValue(mapper.writeValueAsString(collection), new TypeReference<Map<String, Object>>() {
});
} catch (InvalidTypeIdException e) {
throw new IllegalArgumentException("must be a valid FeatureCollection");
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("unable to parse FeatureCollection");
}
}
private ShapeBuilder getShapeBuilderFromObject(Map<String, Object> object) throws IOException {
XContentBuilder contentBuilder = JsonXContent.contentBuilder().value(object);
XContentParser parser = JsonXContent.jsonXContent.createParser(
NamedXContentRegistry.EMPTY, DeprecationHandler.THROW_UNSUPPORTED_OPERATION,
BytesReference.bytes(contentBuilder).streamInput()
);
parser.nextToken();
return ShapeParser.parse(parser);
}
}
\ No newline at end of file
......@@ -321,7 +321,7 @@ public class AttributeParsingServiceImplTest {
Map<String, Object> storageData = new HashMap<>();
storageData.put("location", parseJson(shapeJson));
when(this.geoShapeParser.parseGeoJson(storageData)).thenReturn("");
when(this.geoShapeParser.parseGeoJson(storageData)).thenReturn(new HashMap<>());
Map<String, Object> dataMap = new HashMap<>();
......
......@@ -29,8 +29,7 @@ import java.util.Map;
import java.util.function.Function;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.*;
@RunWith(SpringRunner.class)
public class GeoShapeParserTest {
......@@ -45,124 +44,129 @@ public class GeoShapeParserTest {
public void should_throwException_provided_emptyGeoJson() {
String shapeJson = "{}";
this.validateInput(this.sut::parseGeoJson, shapeJson, "shape type not included");
this.validateInput(this.sut::parseGeoJson, shapeJson, "shape not included");
}
@Test
public void should_throwException_parseInvalidPoint() {
String shapeJson = "{\"type\":\"Point\",\"coordinates\":[-205.01621,39.57422]}";
String shapeJson = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-205.01621,39.57422]}}]}";
this.validateInput(this.sut::parseGeoJson, shapeJson, "Bad X value -205.01621 is not in boundary Rect(minX=-180.0,maxX=180.0,minY=-90.0,maxY=90.0)");
this.validateInput(this.sut::parseGeoJson, shapeJson, "unable to parse FeatureCollection");
}
@Test
public void should_throwException_parseInvalidPoint_NaN() {
String shapeJson = "{\"type\":\"Point\",\"coordinates\":[-205.01621,NaN]}";
String shapeJson = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-205.01621,NaN]}}]}";
this.validateInput(this.sut::parseGeoJson, shapeJson, "geo coordinates must be numbers");
this.validateInput(this.sut::parseGeoJson, shapeJson, "unable to parse FeatureCollection");
}
@Test
public void should_throwException_parseInvalidPoint_missingLatitude() {
String shapeJson = "{\"type\":\"Point\",\"coordinates\":[-205.01621,\"\"]}";
String shapeJson = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-105.01621]}}]}";
this.validateInput(this.sut::parseGeoJson, shapeJson, "geo coordinates must be numbers");
this.validateInput(this.sut::parseGeoJson, shapeJson, "unable to parse FeatureCollection");
}
@Test
public void should_throwException_missingMandatoryAttribute() {
String shapeJson = "{\"mistype\":\"Point\",\"coordinates\":[-205.01621,39.57422]}";
String shapeJson = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"mistype\":\"Point\",\"coordinates\":[-205.01621,39.57422]}}]}";
this.validateInput(this.sut::parseGeoJson, shapeJson, "shape type not included");
this.validateInput(this.sut::parseGeoJson, shapeJson, "must be a valid FeatureCollection");
}
@Test
public void should_throwException_parseInvalidShape() {
String shapeJson = "{\"type\":\"InvalidShape\",\"coordinates\":[-105.01621,39.57422]}";
String shapeJson = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"InvalidShape\",\"coordinates\":[-105.01621,39.57422]}}]}";
this.validateInput(this.sut::parseGeoJson, shapeJson, "unknown geo_shape [InvalidShape]");
this.validateInput(this.sut::parseGeoJson, shapeJson, "must be a valid FeatureCollection");
}
@Test
public void should_parseValidPoint() {
String shapeJson = "{\"type\":\"Point\",\"coordinates\":[-105.01621,39.57422]}";
String shapeJson = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-105.01621,39.57422]}}]}";
this.validateInput(this.sut::parseGeoJson, shapeJson, Strings.EMPTY);
}
@Test
public void should_parseValidMultiPoint() {
String shapeJson = "{\"type\":\"MultiPoint\",\"coordinates\":[[-105.01621,39.57422],[-80.666513,35.053994]]}";
String shapeJson = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"MultiPoint\",\"coordinates\":[[-105.01621,39.57422],[-80.666513,35.053994]]}}]}";
this.validateInput(this.sut::parseGeoJson, shapeJson, Strings.EMPTY);
}
@Test
public void should_parseValidLineString() {
String shapeJson = "{\"type\":\"LineString\",\"coordinates\":[[-101.744384,39.32155],[-101.552124,39.330048],[-101.403808,39.330048],[-101.332397,39.364032],[-101.041259,39.368279],[-100.975341,39.304549],[-100.914916,39.245016],[-100.843505,39.164141],[-100.805053,39.104488],[-100.491943,39.100226],[-100.437011,39.095962],[-100.338134,39.095962],[-100.195312,39.027718],[-100.008544,39.010647],[-99.865722,39.00211],[-99.684448,38.972221],[-99.51416,38.929502],[-99.382324,38.920955],[-99.321899,38.895308],[-99.113159,38.869651],[-99.0802,38.85682],[-98.822021,38.85682],[-98.448486,38.848264],[-98.206787,38.848264],[-98.020019,38.878204],[-97.635498,38.873928]]}";
String shapeJson = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-101.744384,39.32155],[-101.552124,39.330048],[-101.403808,39.330048],[-101.332397,39.364032],[-101.041259,39.368279],[-100.975341,39.304549],[-100.914916,39.245016],[-100.843505,39.164141],[-100.805053,39.104488],[-100.491943,39.100226],[-100.437011,39.095962],[-100.338134,39.095962],[-100.195312,39.027718],[-100.008544,39.010647],[-99.865722,39.00211],[-99.684448,38.972221],[-99.51416,38.929502],[-99.382324,38.920955],[-99.321899,38.895308],[-99.113159,38.869651],[-99.0802,38.85682],[-98.822021,38.85682],[-98.448486,38.848264],[-98.206787,38.848264],[-98.020019,38.878204],[-97.635498,38.873928]]}}]}";
this.validateInput(this.sut::parseGeoJson, shapeJson, Strings.EMPTY);
}
@Test
public void should_parseValidMultiLineString() {
String shapeJson = "{\"type\":\"MultiLineString\",\"coordinates\":[[[-105.021443,39.578057],[-105.021507,39.577809],[-105.021572,39.577495],[-105.021572,39.577164],[-105.021572,39.577032],[-105.021529,39.576784]],[[-105.019898,39.574997],[-105.019598,39.574898],[-105.019061,39.574782]],[[-105.017173,39.574402],[-105.01698,39.574385],[-105.016636,39.574385],[-105.016508,39.574402],[-105.01595,39.57427]],[[-105.014276,39.573972],[-105.014126,39.574038],[-105.013825,39.57417],[-105.01331,39.574452]]]}";
String shapeJson = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"MultiLineString\",\"coordinates\":[[[-105.021443,39.578057],[-105.021507,39.577809],[-105.021572,39.577495],[-105.021572,39.577164],[-105.021572,39.577032],[-105.021529,39.576784]],[[-105.019898,39.574997],[-105.019598,39.574898],[-105.019061,39.574782]],[[-105.017173,39.574402],[-105.01698,39.574385],[-105.016636,39.574385],[-105.016508,39.574402],[-105.01595,39.57427]],[[-105.014276,39.573972],[-105.014126,39.574038],[-105.013825,39.57417],[-105.01331,39.574452]]]}}]}";
this.validateInput(this.sut::parseGeoJson, shapeJson, Strings.EMPTY);
}
@Test
public void should_parseValidPolygon() {
String shapeJson = "{\"type\":\"Polygon\",\"coordinates\":[[[100,0],[101,0],[101,1],[100,1],[100,0]]]}";
String shapeJson = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[100,0],[101,0],[101,1],[100,1],[100,0]]]}}]}";
this.validateInput(this.sut::parseGeoJson, shapeJson, Strings.EMPTY);
}
@Test
public void should_throwException_parseInvalidPolygon_malformedLatitude() {
String shapeJson = "{\"type\":\"Polygon\",\"coordinates\":[[[100,\"afgg\"],[101,0],[101,1],[100,1],[100,0]]]}";
String shapeJson = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[100,\"afgg\"],[101,0],[101,1],[100,1],[100,0]]]}}]}";
this.validateInput(this.sut::parseGeoJson, shapeJson, "geo coordinates must be numbers");
this.validateInput(this.sut::parseGeoJson, shapeJson, "unable to parse FeatureCollection");
}
@Test
public void should_parseValidMultiPolygon() {
String shapeJson = "{\"type\":\"MultiPolygon\",\"coordinates\":[[[[107,7],[108,7],[108,8],[107,8],[107,7]]],[[[100,0],[101,0],[101,1],[100,1],[100,0]]]]}";
String shapeJson = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"MultiPolygon\",\"coordinates\":[[[[107,7],[108,7],[108,8],[107,8],[107,7]]],[[[100,0],[101,0],[101,1],[100,1],[100,0]]]]}}]}";
this.validateInput(this.sut::parseGeoJson, shapeJson, Strings.EMPTY);
}
@Test
public void should_parseValidGeometryCollection() {
String shapeJson = "{\"type\":\"GeometryCollection\",\"geometries\":[{\"type\":\"Point\",\"coordinates\":[-80.660805,35.049392]},{\"type\":\"Polygon\",\"coordinates\":[[[-80.664582,35.044965],[-80.663874,35.04428],[-80.662586,35.04558],[-80.663444,35.046036],[-80.664582,35.044965]]]},{\"type\":\"LineString\",\"coordinates\":[[-80.662372,35.059509],[-80.662693,35.059263],[-80.662844,35.05893],[-80.66308,35.058332],[-80.663595,35.057753],[-80.663874,35.057401],[-80.66441,35.057033],[-80.664861,35.056787],[-80.665419,35.056506],[-80.665633,35.056312],[-80.666019,35.055891],[-80.666191,35.055452],[-80.666191,35.055171],[-80.666255,35.05489],[-80.666213,35.054222],[-80.666213,35.053924],[-80.665955,35.052905],[-80.665698,35.052044],[-80.665504,35.051482],[-80.665762,35.050481],[-80.66617,35.049725],[-80.666513,35.049286],[-80.666921,35.048531],[-80.667006,35.048215],[-80.667071,35.047775],[-80.667049,35.047389],[-80.666964,35.046985],[-80.666813,35.046353],[-80.666599,35.045966],[-80.666406,35.045615],[-80.665998,35.045193],[-80.665526,35.044877],[-80.664989,35.044543],[-80.664496,35.044174],[-80.663852,35.043876],[-80.663037,35.043717]]}]}";
String shapeJson = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"GeometryCollection\",\"geometries\":[{\"type\":\"Point\",\"coordinates\":[-80.660805,35.049392]},{\"type\":\"Polygon\",\"coordinates\":[[[-80.664582,35.044965],[-80.663874,35.04428],[-80.662586,35.04558],[-80.663444,35.046036],[-80.664582,35.044965]]]},{\"type\":\"LineString\",\"coordinates\":[[-80.662372,35.059509],[-80.662693,35.059263],[-80.662844,35.05893],[-80.66308,35.058332],[-80.663595,35.057753],[-80.663874,35.057401],[-80.66441,35.057033],[-80.664861,35.056787],[-80.665419,35.056506],[-80.665633,35.056312],[-80.666019,35.055891],[-80.666191,35.055452],[-80.666191,35.055171],[-80.666255,35.05489],[-80.666213,35.054222],[-80.666213,35.053924],[-80.665955,35.052905],[-80.665698,35.052044],[-80.665504,35.051482],[-80.665762,35.050481],[-80.66617,35.049725],[-80.666513,35.049286],[-80.666921,35.048531],[-80.667006,35.048215],[-80.667071,35.047775],[-80.667049,35.047389],[-80.666964,35.046985],[-80.666813,35.046353],[-80.666599,35.045966],[-80.666406,35.045615],[-80.665998,35.045193],[-80.665526,35.044877],[-80.664989,35.044543],[-80.664496,35.044174],[-80.663852,35.043876],[-80.663037,35.043717]]}]}}]}";
this.validateInput(this.sut::parseGeoJson, shapeJson, Strings.EMPTY);
}
@Test
public void should_throwException_parseUnsupportedType_featureCollection() {
public void should_parseValidFeatureCollection() {
String shapeJson = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[1.9496737127045984,58.41415669686543],[1.8237672363511042,58.42946998193435],[1.8422735102001124,58.471472136376455],[1.9683241046247606,58.45614207250076],[1.9496737127045984,58.41415669686543]]]}}]}";
this.validateInput(this.sut::parseGeoJson, shapeJson, "unknown geo_shape [FeatureCollection]");
this.validateInput(this.sut::parseGeoJson, shapeJson, Strings.EMPTY);
}
@Test
public void should_throwException_parseUnsupportedType_feature() {
String shapeJson = "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-80.724878,35.265454],[-80.722646,35.260338],[-80.720329,35.260618],[-80.718698,35.260267],[-80.715093,35.260548],[-80.71681,35.255361],[-80.710887,35.255361],[-80.703248,35.265033],[-80.704793,35.268397],[-80.70857,35.268257],[-80.712518,35.270359],[-80.715179,35.267696],[-80.721359,35.267276],[-80.724878,35.265454]]]},\"properties\":{\"name\":\"Plaza Road Park\"}}";
this.validateInput(this.sut::parseGeoJson, shapeJson, "unknown geo_shape [Feature]");
this.validateInput(this.sut::parseGeoJson, shapeJson, "must be a valid FeatureCollection");
}
private void validateInput(Function<Map<String, Object>, String> parser, String shapeJson, String errorMessage) {
private void validateInput(Function<Map<String, Object>, Map<String, Object>> parser, String shapeJson, String errorMessage) {
try {
Type type = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> shapeObj = new Gson().fromJson(shapeJson, type);
String parsedShape = parser.apply(shapeObj);
Map<String, Object> parsedShape = parser.apply(shapeObj);
assertNotNull(parsedShape);
assertTrue(Strings.isNullOrEmpty(errorMessage));
} catch (IllegalArgumentException e) {
assertThat(String.format("Incorrect error message for geo-json parsing [ %s ]", shapeJson),
e.getMessage(), containsString(errorMessage));
if (Strings.isNullOrEmpty(errorMessage)) {
fail(String.format("error parsing valid geo-json %s", shapeJson));
} else {
assertThat(String.format("Incorrect error message for geo-json parsing [ %s ]", shapeJson),
e.getMessage(), containsString(errorMessage));
}
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment