GeometryJSON.java
/* This file is part of Openrouteservice.
*
* Openrouteservice is free software; you can redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License along with this library;
* if not, see <https://www.gnu.org/licenses/>.
*/
package org.heigit.ors.geojson;
import org.geotools.geometry.jts.coordinatesequence.CoordinateSequences;
import org.heigit.ors.util.FormatUtility;
import org.json.JSONArray;
import org.json.JSONObject;
import org.locationtech.jts.geom.*;
@SuppressWarnings("unchecked")
public class GeometryJSON {
private static final int COORDINATE_PRECISION = 6;
private static final GeometryFactory factory = new GeometryFactory();
private GeometryJSON() {
}
public static org.json.simple.JSONArray toJSON(Polygon poly) {
org.json.simple.JSONArray coords = new org.json.simple.JSONArray();
LineString shell = poly.getExteriorRing();
boolean inverse = shell.getNumPoints() > 1 && !CoordinateSequences.isCCW(shell.getCoordinateSequence());
coords.add(toJSON(shell, inverse));
if (poly.getNumInteriorRing() > 0) {
int nRings = poly.getNumInteriorRing();
for (int j = 0; j < nRings; ++j) {
LineString ring = poly.getInteriorRingN(j);
inverse = ring.getNumPoints() > 1 && CoordinateSequences.isCCW(ring.getCoordinateSequence());
coords.add(toJSON(ring, inverse));
}
}
return coords;
}
private static org.json.simple.JSONArray toJSON(LineString line, boolean inverseSeq) {
// "coordinates": [ [100.0, 0.0], [101.0, 1.0] ]
int size = line.getNumPoints();
org.json.simple.JSONArray arrCoords = new org.json.simple.JSONArray();
CoordinateSequence seq = line.getCoordinateSequence();
for (int i = 0; i < size; ++i) {
Coordinate coord = seq.getCoordinate(inverseSeq ? size - i - 1 : i);
arrCoords.add(toJSON(coord));
}
return arrCoords;
}
private static org.json.simple.JSONArray toJSON(Point point) {
return toJSON(point.getCoordinate());
}
public static org.json.simple.JSONArray toJSON(Coordinate c) {
org.json.simple.JSONArray arrCoords = new org.json.simple.JSONArray();
arrCoords.add(FormatUtility.roundToDecimals(c.x, COORDINATE_PRECISION));
arrCoords.add(FormatUtility.roundToDecimals(c.y, COORDINATE_PRECISION));
return arrCoords;
}
public static org.json.simple.JSONArray toJSON(Coordinate[] coords, boolean includeElevation) {
org.json.simple.JSONArray arrCoords = new org.json.simple.JSONArray();
for (Coordinate c : coords) {
org.json.simple.JSONArray coord = new org.json.simple.JSONArray();
coord.add(FormatUtility.roundToDecimals(c.x, COORDINATE_PRECISION));
coord.add(FormatUtility.roundToDecimals(c.y, COORDINATE_PRECISION));
if (includeElevation)
coord.add(FormatUtility.roundToDecimals(c.z, 1));
arrCoords.add(coord);
}
return arrCoords;
}
public static Geometry parse(JSONObject json) throws Exception {
if (!json.has("type"))
throw new Exception("type element is missing.");
if (!json.has("coordinates"))
throw new Exception("coordinates element is missing.");
String type = json.getString("type");
JSONArray arrCoords = json.getJSONArray("coordinates");
return switch (type) {
case "Point" -> readPoint(arrCoords);
case "MultiPoint" -> readMultiPoint(arrCoords);
case "LineString" -> readLineString(arrCoords);
case "MultiLineString" -> readMultiLineString(arrCoords);
case "Polygon" -> readPolygon(arrCoords);
case "MultiPolygon" -> readMultiPolygon(arrCoords);
default -> throw new Exception("invalid type: " + type);
};
}
private static Point readPoint(JSONArray value) {
Coordinate c = new Coordinate(value.getDouble(0), value.getDouble(1));
return factory.createPoint(c);
}
private static MultiPoint readMultiPoint(JSONArray value) {
return factory.createMultiPointFromCoords(readCoordinates(value));
}
private static LineString readLineString(JSONArray value) {
return factory.createLineString(readCoordinates(value));
}
private static MultiLineString readMultiLineString(JSONArray value) {
int n = value.length();
LineString[] lineStrings = new LineString[n];
for (int i = 0; i < n; i++) {
JSONArray arrLineString = (JSONArray) value.get(i);
lineStrings[i] = readLineString(arrLineString);
}
return factory.createMultiLineString(lineStrings);
}
private static MultiPolygon readMultiPolygon(JSONArray value) {
int n = value.length();
Polygon[] polys = new Polygon[n];
for (int i = 0; i < n; i++) {
JSONArray arrPoly = (JSONArray) value.get(i);
polys[i] = readPolygon(arrPoly);
}
return factory.createMultiPolygon(polys);
}
private static Polygon readPolygon(JSONArray value) {
int n = value.length();
LinearRing shell = null;
LinearRing[] holes = new LinearRing[n - 1];
for (int i = 0; i < n; i++) {
JSONArray arrLineString = (JSONArray) value.get(i);
if (i == 0)
shell = factory.createLinearRing(readCoordinates(arrLineString));
else
holes[i - 1] = factory.createLinearRing(readCoordinates(arrLineString));
}
if (holes.length == 0)
return factory.createPolygon(shell);
else
return factory.createPolygon(shell, holes);
}
private static Coordinate[] readCoordinates(JSONArray value) {
int n = value.length();
Coordinate[] coords = new Coordinate[n];
for (int i = 0; i < n; i++) {
JSONArray arrCoord = value.getJSONArray(i);
coords[i] = new Coordinate(arrCoord.getDouble(0), arrCoord.getDouble(1));
}
return coords;
}
}