GeoJSONReader.java
package org.cugos.wkg;
import org.antlr.v4.runtime.*;
import org.cugos.wkg.internal.JSONBaseListener;
import org.cugos.wkg.internal.JSONLexer;
import org.cugos.wkg.internal.JSONParser;
import java.util.*;
/**
* Read a Geometry from a GeoJSON String
* @author Jared Erickson
*/
public class GeoJSONReader implements Reader<String> {
/**
* The default SRID for GeoJSON is EPSG:4326
*/
private final String srid = "4326";
/**
* Read a Geometry from a GeoJSON String
* @param jsonStr The GeoJSON String
* @return The Geometry or null
*/
@Override
public Geometry read(String jsonStr) {
JSON json = parse(jsonStr);
Geometry geometry = null;
if (json instanceof JSONObject) {
JSONObject jsonObject = (JSONObject) json;
String type = jsonObject.get("type").toString();
if (type.equalsIgnoreCase("feature")) {
geometry = readGeometry((JSONObject) jsonObject.get("geometry")).setData(((JSONObject) jsonObject.get("properties")).values());
} else if (type.equalsIgnoreCase("featurecollection")) {
JSONArray jsonArray = (JSONArray) jsonObject.get("features");
List<Geometry> geometries = new ArrayList<>();
for (Object feature : jsonArray.values()) {
JSONObject featureJSONObject = (JSONObject) feature;
JSONObject geometryJSONObject = (JSONObject) (featureJSONObject.get("geometry"));
geometries.add(readGeometry(geometryJSONObject).setData(((JSONObject) featureJSONObject.get("properties")).values()));
}
geometry = new GeometryCollection(geometries, geometries.isEmpty() ? Dimension.Two : geometries.get(0).getDimension(), srid);
} else {
geometry = readGeometry(jsonObject);
}
}
return geometry;
}
@Override
public String getName() {
return "GeoJSON";
}
private Point readPoint(JSONObject jsonObject) {
Coordinate coordinate = getCoordinate((JSONArray) jsonObject.get("coordinates"));
return new Point(coordinate, coordinate.getDimension(), srid);
}
private LineString readLineString(JSONObject jsonObject) {
List<Coordinate>coordinates = getCoordinates((JSONArray) jsonObject.get("coordinates"));
return new LineString(coordinates, coordinates.size() > 0 ? coordinates.get(0).getDimension() : Dimension.Two, srid);
}
private Polygon readPolygon(JSONObject jsonObject) {
List<List<Coordinate>> coordinateSets = getCoordinateSets((JSONArray) jsonObject.get("coordinates"));
if (coordinateSets.size() > 0) {
LinearRing exteriorRing = getLinearRing(coordinateSets.get(0));
List<LinearRing> interiorRings = new ArrayList<>();
for (List<Coordinate> coordinates : coordinateSets.subList(1, coordinateSets.size())) {
interiorRings.add(getLinearRing(coordinates));
}
return new Polygon(exteriorRing, interiorRings, exteriorRing.getDimension(), srid);
} else {
return Polygon.createEmpty();
}
}
private MultiPoint readMultiPoint(JSONObject jsonObject) {
List<Point> points = getPoints((JSONArray) jsonObject.get("coordinates"));
return new MultiPoint(points, points.size() > 0 ? points.get(0).getDimension() : Dimension.Two, srid);
}
private MultiLineString readMultiLineString(JSONObject jsonObject) {
JSONArray coordinates = (JSONArray) jsonObject.get("coordinates");
List<LineString> lineStrings = new ArrayList<>();
for(Object coords : coordinates.values()) {
List<Coordinate> c = getCoordinates((JSONArray) coords);
lineStrings.add(new LineString(c, c.size() > 0 ? c.get(0).getDimension() : Dimension.Two, srid));
}
return new MultiLineString(lineStrings, lineStrings.size() > 0 ? lineStrings.get(0).getDimension() : Dimension.Two, srid);
}
private MultiPolygon readMultiPolygon(JSONObject jsonObject) {
JSONArray coordinates = (JSONArray) jsonObject.get("coordinates");
List<Polygon> polygons = new ArrayList<>();
for(Object polygonsCoords : coordinates.values()) {
List<List<Coordinate>> coordinateSets = getCoordinateSets((JSONArray) polygonsCoords);
if (coordinateSets.size() > 0) {
LinearRing exteriorRing = getLinearRing(coordinateSets.get(0));
List<LinearRing> interiorRings = new ArrayList<>();
for (List<Coordinate> coords : coordinateSets.subList(1, coordinateSets.size())) {
interiorRings.add(getLinearRing(coords));
}
polygons.add(new Polygon(exteriorRing, interiorRings, exteriorRing.getDimension(), srid));
} else {
polygons.add(Polygon.createEmpty());
}
}
return new MultiPolygon(polygons, polygons.size() > 0 ? polygons.get(0).getDimension() : Dimension.Two, srid);
}
private GeometryCollection readGeometryCollection(JSONObject jsonObject) {
JSONArray geometriesJsonArray = (JSONArray) jsonObject.get("geometries");
List<Geometry> geometries = new ArrayList<>();
for(Object geometryJsonObject : geometriesJsonArray.values()) {
geometries.add(readGeometry((JSONObject) geometryJsonObject));
}
return new GeometryCollection(geometries, geometries.size() > 0 ? geometries.get(0).getDimension() : Dimension.Two, srid);
}
private Geometry readGeometry(JSONObject jsonObject) {
String type = (String) jsonObject.get("type");
Geometry geometry = null;
if (type != null) {
if (type.equalsIgnoreCase("point")) {
geometry = readPoint(jsonObject);
} else if (type.equalsIgnoreCase("LineString")) {
geometry = readLineString(jsonObject);
} else if (type.equalsIgnoreCase("Polygon")) {
geometry = readPolygon(jsonObject);
} else if (type.equalsIgnoreCase("MultiPoint")) {
geometry = readMultiPoint(jsonObject);
} else if (type.equalsIgnoreCase("MultiLineString")) {
geometry = readMultiLineString(jsonObject);
} else if (type.equalsIgnoreCase("MultiPolygon")) {
geometry = readMultiPolygon(jsonObject);
} else if (type.equalsIgnoreCase("GeometryCollection")) {
geometry = readGeometryCollection(jsonObject);
}
}
return geometry;
}
private LinearRing getLinearRing(List<Coordinate> coordinates) {
return new LinearRing(coordinates, coordinates.size() > 0 ? coordinates.get(0).getDimension() : Dimension.Two, srid);
}
private Coordinate getCoordinate(JSONArray jsonArray) {
if (jsonArray.values().size() > 2) {
return Coordinate.create3D(getDouble(jsonArray.get(0)), getDouble(jsonArray.get(1)), getDouble(jsonArray.get(2)));
} else if (jsonArray.values().size() == 2) {
return Coordinate.create2D(getDouble(jsonArray.get(0)), getDouble(jsonArray.get(1)));
} else {
return Coordinate.createEmpty();
}
}
private Double getDouble(Object value) {
if (value instanceof Double) {
return (Double) value;
} else if (value instanceof Integer) {
return ((Integer) value).doubleValue();
} else if (value != null) {
return Double.parseDouble(value.toString());
} else {
return Double.NaN;
}
}
private List<Coordinate> getCoordinates(JSONArray jsonArray) {
List<Coordinate> coordinates = new ArrayList<>();
for(Object value : jsonArray.values()) {
if (value instanceof JSONArray) {
coordinates.add(getCoordinate((JSONArray)value));
}
}
return coordinates;
}
private List<Point> getPoints(JSONArray jsonArray) {
List<Point> points = new ArrayList<>();
for(Object value : jsonArray.values()) {
if (value instanceof JSONArray) {
Coordinate coordinate = getCoordinate((JSONArray)value);
points.add(new Point(coordinate, coordinate.getDimension()));
}
}
return points;
}
private List<List<Coordinate>> getCoordinateSets(JSONArray jsonArray) {
List<List<Coordinate>> coordinateSets = new ArrayList<>();
for(Object value : jsonArray.values()) {
if (value instanceof JSONArray) {
coordinateSets.add(getCoordinates((JSONArray)value));
}
}
return coordinateSets;
}
private JSON parse(String jsonStr) {
JSONLexer lexer = new JSONLexer(new ANTLRInputStream(jsonStr));
JSONParser parser = new JSONParser(new CommonTokenStream(lexer));
parser.addErrorListener(new BaseErrorListener() {
@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
throw new IllegalStateException("failed to parse at line " + line + " due to " + msg, e);
}
});
final Stack<JSON> json = new Stack<>();
parser.addParseListener(new JSONBaseListener() {
@Override
public void enterObj(JSONParser.ObjContext ctx) {
json.push(new JSONObject());
}
@Override
public void exitPair(JSONParser.PairContext ctx) {
Object value = getValueFromValueContent(ctx.value());
JSONObject jsonObject = (JSONObject) json.peek();
jsonObject.values().put(removeDoubleQuotes(ctx.STRING().getText()), value);
}
@Override
public void enterArray(JSONParser.ArrayContext ctx) {
json.push(new JSONArray());
}
@Override
public void exitArray(JSONParser.ArrayContext ctx) {
List<Object> values = new ArrayList<>();
for(JSONParser.ValueContext valueContext : ctx.value()) {
Object value = getValueFromValueContent(valueContext);
if (value instanceof JSON) {
values.add(0, value);
} else {
values.add(value);
}
}
JSONArray jsonArray = (JSONArray) json.peek();
jsonArray.values().addAll(values);
}
private String removeDoubleQuotes(String str) {
if (str != null) {
return str.replaceAll("\"", "");
} else {
return null;
}
}
private Number parseNumber(String value) {
if (value.contains(".")) {
return Double.parseDouble(value);
} else {
return Integer.parseInt(value);
}
}
private Object getValueFromValueContent(JSONParser.ValueContext ctx) {
if (ctx.obj() != null) {
return json.pop();
} else if (ctx.array() != null) {
return json.pop();
} else if (ctx.STRING() != null) {
return removeDoubleQuotes(ctx.STRING().getText());
} else if (ctx.NUMBER() != null) {
return parseNumber(removeDoubleQuotes(ctx.NUMBER().getText()));
} else {
return null;
}
}
});
parser.json();
return json.pop();
}
private static interface JSON {
}
private static class JSONObject implements JSON {
private Map<String,Object> values = new LinkedHashMap<>();
public Map<String,Object> values() {
return values;
}
public Object get(String key) {
return values.get(key);
}
@Override
public String toString() {
return values.toString();
}
}
private static class JSONArray implements JSON {
private List<Object> values = new ArrayList<>();
public List<Object> values() {
return values;
}
public Object get(int index) {
return values.get(index);
}
@Override
public String toString() {
return values.toString();
}
}
}