GeometryJSON.java

  1. /*  This file is part of Openrouteservice.
  2.  *
  3.  *  Openrouteservice is free software; you can redistribute it and/or modify it under the terms of the
  4.  *  GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1
  5.  *  of the License, or (at your option) any later version.

  6.  *  This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  7.  *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8.  *  See the GNU Lesser General Public License for more details.

  9.  *  You should have received a copy of the GNU Lesser General Public License along with this library;
  10.  *  if not, see <https://www.gnu.org/licenses/>.
  11.  */
  12. package org.heigit.ors.geojson;

  13. import org.geotools.geometry.jts.coordinatesequence.CoordinateSequences;
  14. import org.heigit.ors.util.FormatUtility;
  15. import org.json.JSONArray;
  16. import org.json.JSONObject;
  17. import org.locationtech.jts.geom.*;

  18. @SuppressWarnings("unchecked")
  19. public class GeometryJSON {

  20.     private static final int COORDINATE_PRECISION = 6;
  21.     private static final GeometryFactory factory = new GeometryFactory();

  22.     private GeometryJSON() {
  23.     }

  24.     public static org.json.simple.JSONArray toJSON(Polygon poly) {
  25.         org.json.simple.JSONArray coords = new org.json.simple.JSONArray();

  26.         LineString shell = poly.getExteriorRing();

  27.         boolean inverse = shell.getNumPoints() > 1 && !CoordinateSequences.isCCW(shell.getCoordinateSequence());
  28.         coords.add(toJSON(shell, inverse));

  29.         if (poly.getNumInteriorRing() > 0) {
  30.             int nRings = poly.getNumInteriorRing();

  31.             for (int j = 0; j < nRings; ++j) {
  32.                 LineString ring = poly.getInteriorRingN(j);
  33.                 inverse = ring.getNumPoints() > 1 && CoordinateSequences.isCCW(ring.getCoordinateSequence());
  34.                 coords.add(toJSON(ring, inverse));
  35.             }
  36.         }

  37.         return coords;
  38.     }

  39.     private static org.json.simple.JSONArray toJSON(LineString line, boolean inverseSeq) {
  40.         // "coordinates": [ [100.0, 0.0], [101.0, 1.0] ]
  41.         int size = line.getNumPoints();

  42.         org.json.simple.JSONArray arrCoords = new org.json.simple.JSONArray();

  43.         CoordinateSequence seq = line.getCoordinateSequence();

  44.         for (int i = 0; i < size; ++i) {
  45.             Coordinate coord = seq.getCoordinate(inverseSeq ? size - i - 1 : i);
  46.             arrCoords.add(toJSON(coord));
  47.         }

  48.         return arrCoords;
  49.     }

  50.     private static org.json.simple.JSONArray toJSON(Point point) {
  51.         return toJSON(point.getCoordinate());
  52.     }

  53.     public static org.json.simple.JSONArray toJSON(Coordinate c) {
  54.         org.json.simple.JSONArray arrCoords = new org.json.simple.JSONArray();
  55.         arrCoords.add(FormatUtility.roundToDecimals(c.x, COORDINATE_PRECISION));
  56.         arrCoords.add(FormatUtility.roundToDecimals(c.y, COORDINATE_PRECISION));

  57.         return arrCoords;
  58.     }

  59.     public static org.json.simple.JSONArray toJSON(Coordinate[] coords, boolean includeElevation) {
  60.         org.json.simple.JSONArray arrCoords = new org.json.simple.JSONArray();
  61.         for (Coordinate c : coords) {
  62.             org.json.simple.JSONArray coord = new org.json.simple.JSONArray();
  63.             coord.add(FormatUtility.roundToDecimals(c.x, COORDINATE_PRECISION));
  64.             coord.add(FormatUtility.roundToDecimals(c.y, COORDINATE_PRECISION));
  65.             if (includeElevation)
  66.                 coord.add(FormatUtility.roundToDecimals(c.z, 1));
  67.             arrCoords.add(coord);
  68.         }
  69.         return arrCoords;
  70.     }

  71.     public static Geometry parse(JSONObject json) throws Exception {
  72.         if (!json.has("type"))
  73.             throw new Exception("type element is missing.");

  74.         if (!json.has("coordinates"))
  75.             throw new Exception("coordinates element is missing.");

  76.         String type = json.getString("type");
  77.         JSONArray arrCoords = json.getJSONArray("coordinates");
  78.         return switch (type) {
  79.             case "Point" -> readPoint(arrCoords);
  80.             case "MultiPoint" -> readMultiPoint(arrCoords);
  81.             case "LineString" -> readLineString(arrCoords);
  82.             case "MultiLineString" -> readMultiLineString(arrCoords);
  83.             case "Polygon" -> readPolygon(arrCoords);
  84.             case "MultiPolygon" -> readMultiPolygon(arrCoords);
  85.             default -> throw new Exception("invalid type: " + type);
  86.         };
  87.     }

  88.     private static Point readPoint(JSONArray value) {
  89.         Coordinate c = new Coordinate(value.getDouble(0), value.getDouble(1));
  90.         return factory.createPoint(c);
  91.     }

  92.     private static MultiPoint readMultiPoint(JSONArray value) {
  93.         return factory.createMultiPointFromCoords(readCoordinates(value));
  94.     }

  95.     private static LineString readLineString(JSONArray value) {
  96.         return factory.createLineString(readCoordinates(value));
  97.     }

  98.     private static MultiLineString readMultiLineString(JSONArray value) {
  99.         int n = value.length();
  100.         LineString[] lineStrings = new LineString[n];

  101.         for (int i = 0; i < n; i++) {
  102.             JSONArray arrLineString = (JSONArray) value.get(i);
  103.             lineStrings[i] = readLineString(arrLineString);
  104.         }

  105.         return factory.createMultiLineString(lineStrings);
  106.     }

  107.     private static MultiPolygon readMultiPolygon(JSONArray value) {
  108.         int n = value.length();
  109.         Polygon[] polys = new Polygon[n];

  110.         for (int i = 0; i < n; i++) {
  111.             JSONArray arrPoly = (JSONArray) value.get(i);
  112.             polys[i] = readPolygon(arrPoly);
  113.         }

  114.         return factory.createMultiPolygon(polys);
  115.     }

  116.     private static Polygon readPolygon(JSONArray value) {
  117.         int n = value.length();

  118.         LinearRing shell = null;
  119.         LinearRing[] holes = new LinearRing[n - 1];

  120.         for (int i = 0; i < n; i++) {
  121.             JSONArray arrLineString = (JSONArray) value.get(i);
  122.             if (i == 0)
  123.                 shell = factory.createLinearRing(readCoordinates(arrLineString));
  124.             else
  125.                 holes[i - 1] = factory.createLinearRing(readCoordinates(arrLineString));
  126.         }

  127.         if (holes.length == 0)
  128.             return factory.createPolygon(shell);
  129.         else
  130.             return factory.createPolygon(shell, holes);
  131.     }

  132.     private static Coordinate[] readCoordinates(JSONArray value) {
  133.         int n = value.length();

  134.         Coordinate[] coords = new Coordinate[n];

  135.         for (int i = 0; i < n; i++) {
  136.             JSONArray arrCoord = value.getJSONArray(i);
  137.             coords[i] = new Coordinate(arrCoord.getDouble(0), arrCoord.getDouble(1));
  138.         }

  139.         return coords;
  140.     }
  141. }