All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.locationtech.spatial4j.io.GeoJSONReader Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2015 VoyagerSearch and others
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Apache License, Version 2.0 which
 * accompanies this distribution and is available at
 *    http://www.apache.org/licenses/LICENSE-2.0.txt
 ******************************************************************************/

package org.locationtech.spatial4j.io;

import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.context.SpatialContextFactory;
import org.locationtech.spatial4j.distance.DistanceUtils;
import org.locationtech.spatial4j.exception.InvalidShapeException;
import org.locationtech.spatial4j.shape.Circle;
import org.locationtech.spatial4j.shape.Point;
import org.locationtech.spatial4j.shape.Shape;
import org.locationtech.spatial4j.shape.ShapeFactory;
import org.noggit.JSONParser;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;

public class GeoJSONReader implements ShapeReader {

  protected static final String BUFFER = "buffer";
  protected static final String BUFFER_UNITS = "buffer_units";

  protected final SpatialContext ctx;
  protected final ShapeFactory shapeFactory;

  public GeoJSONReader(SpatialContext ctx, SpatialContextFactory factory) {
    this.ctx = ctx;
    this.shapeFactory = ctx.getShapeFactory();
  }

  @Override
  public String getFormatName() {
    return ShapeIO.GeoJSON;
  }

  @Override
  public final Shape read(Reader reader) throws IOException, ParseException {
    return readShape(new JSONParser(reader));
  }

  @Override
  public Shape read(Object value) throws IOException, ParseException, InvalidShapeException {
    String v = value.toString().trim();
    return read(new StringReader(v));
  }

  @Override
  public Shape readIfSupported(Object value) throws InvalidShapeException {
    String v = value.toString().trim();
    if (!(v.startsWith("{") && v.endsWith("}"))) {
      return null;
    }
    try {
      return read(new StringReader(v));
    } catch (IOException ex) {
    } catch (ParseException e) {
    }
    return null;
  }

  // --------------------------------------------------------------
  // Read GeoJSON
  // --------------------------------------------------------------


  protected void readCoordXYZ(JSONParser parser, ShapeFactory.PointsBuilder pointsBuilder) throws IOException, ParseException {
    assert (parser.lastEvent() == JSONParser.ARRAY_START);

    double x = Double.NaN, y = Double.NaN, z = Double.NaN;
    int idx = 0;

    int evt = parser.nextEvent();
    while (evt != JSONParser.EOF) {
      switch (evt) {
        case JSONParser.LONG:
        case JSONParser.NUMBER:
        case JSONParser.BIGNUMBER:
          double value = parser.getDouble();
          switch(idx) {
            case 0: x = value; break;
            case 1: y = value; break;
            case 2: z = value; break;
          }
          idx++;
          break;

        case JSONParser.ARRAY_END:
          if (idx <= 2) { // don't have a 'z'
            pointsBuilder.pointXY(shapeFactory.normX(x), shapeFactory.normY(y));
          } else {
            pointsBuilder.pointXYZ(shapeFactory.normX(x), shapeFactory.normY(y), shapeFactory.normZ(z));
          }
          return;

        case JSONParser.STRING:
        case JSONParser.BOOLEAN:
        case JSONParser.NULL:
        case JSONParser.OBJECT_START:
        case JSONParser.OBJECT_END:
        case JSONParser.ARRAY_START:
        default:
          throw new ParseException("Unexpected " + JSONParser.getEventString(evt),
              (int) parser.getPosition());
      }
      evt = parser.nextEvent();
    }
    return;
  }

  protected void readCoordListXYZ(JSONParser parser, ShapeFactory.PointsBuilder pointsBuilder) throws IOException, ParseException {
    assert (parser.lastEvent() == JSONParser.ARRAY_START);

    int evt = parser.nextEvent();
    while (evt != JSONParser.EOF) {
      switch (evt) {
        case JSONParser.ARRAY_START:
          readCoordXYZ(parser, pointsBuilder); // reads until ARRAY_END
          break;

        case JSONParser.ARRAY_END:
          return;

        default:
          throw new ParseException("Unexpected " + JSONParser.getEventString(evt),
              (int) parser.getPosition());
      }
      evt = parser.nextEvent();
    }
  }

  protected void readUntilEvent(JSONParser parser, final int event) throws IOException {
    int evt = parser.lastEvent();
    while (true) {
      if (evt == event || evt == JSONParser.EOF) {
        return;
      }
      evt = parser.nextEvent();
    }
  }

  protected Shape readPoint(JSONParser parser) throws IOException, ParseException {
    assert (parser.lastEvent() == JSONParser.ARRAY_START);
    OnePointsBuilder onePointsBuilder = new OnePointsBuilder(shapeFactory);
    readCoordXYZ(parser, onePointsBuilder);
    Point point = onePointsBuilder.getPoint();
    readUntilEvent(parser, JSONParser.OBJECT_END);
    return point;
  }

  protected Shape readLineString(JSONParser parser) throws IOException, ParseException {
    assert (parser.lastEvent() == JSONParser.ARRAY_START);
    ShapeFactory.LineStringBuilder builder = shapeFactory.lineString();
    readCoordListXYZ(parser, builder);

    // check for buffer field
    builder.buffer(readDistance(BUFFER, BUFFER_UNITS, parser));

    Shape out = builder.build();
    readUntilEvent(parser, JSONParser.OBJECT_END);
    return out;
  }

  protected Circle readCircle(JSONParser parser) throws IOException, ParseException {
    assert (parser.lastEvent() == JSONParser.ARRAY_START);
    OnePointsBuilder onePointsBuilder = new OnePointsBuilder(shapeFactory);
    readCoordXYZ(parser, onePointsBuilder);
    Point point = onePointsBuilder.getPoint();

    return shapeFactory.circle(point, readDistance("radius", "radius_units", parser));
  }

  /**
   * Helper method to read a up until a distance value (radius, buffer) and it's corresponding unit are found.
   * 

* This method returns 0 if no distance value is found. This method currently only handles distance units of "km". *

* @param distProperty The name of the property containing the distance value. * @param distUnitsProperty The name of the property containing the distance unit. */ protected double readDistance(String distProperty, String distUnitsProperty, JSONParser parser) throws IOException { double dist = 0; String key = null; int event = JSONParser.OBJECT_END; int evt = parser.lastEvent(); while (true) { if (evt == event || evt == JSONParser.EOF) { break; } evt = parser.nextEvent(); if(parser.wasKey()) { key = parser.getString(); } else if(evt==JSONParser.NUMBER || evt==JSONParser.LONG) { if(distProperty.equals(key)) { dist = parser.getDouble(); } } else if(evt==JSONParser.STRING) { if(distUnitsProperty.equals(key)) { String units = parser.getString(); //TODO: support for more units? if("km".equals(units)) { // Convert KM to degrees dist = DistanceUtils.dist2Degrees(dist, DistanceUtils.EARTH_MEAN_RADIUS_KM); } } } } return shapeFactory.normDist(dist); } protected Shape readShape(JSONParser parser) throws IOException, ParseException { String type = null; String key = null; int evt = parser.nextEvent(); while (evt != JSONParser.EOF) { switch (evt) { case JSONParser.STRING: if (parser.wasKey()) { key = parser.getString(); } else { if ("type".equals(key)) { type = parser.getString(); } else { throw new ParseException("Unexpected String Value for key: " + key, (int) parser.getPosition()); } } break; case JSONParser.ARRAY_START: if ("coordinates".equals(key)) { Shape shape = readShapeFromCoordinates(type, parser); readUntilEvent(parser, JSONParser.OBJECT_END); return shape; } else if ("geometries".equals(key)) { List shapes = new ArrayList(); int sub = parser.nextEvent(); while (sub != JSONParser.EOF) { if (sub == JSONParser.OBJECT_START) { Shape s = readShape(parser); if (s != null) { shapes.add(s); } } else if (sub == JSONParser.OBJECT_END) { break; } sub = parser.nextEvent(); } if (shapes.isEmpty()) { throw new ParseException("Shape Collection with no geometries!", (int) parser.getPosition()); } return ctx.makeCollection(shapes); } else { throw new ParseException("Unknown type: "+type, (int) parser.getPosition()); } case JSONParser.ARRAY_END: break; case JSONParser.OBJECT_START: if (key != null) { // System.out.println("Unexpected object: " + key); } break; case JSONParser.LONG: case JSONParser.NUMBER: case JSONParser.BIGNUMBER: case JSONParser.BOOLEAN: case JSONParser.NULL: case JSONParser.OBJECT_END: // System.out.println(">>>>>" + JSONParser.getEventString(evt) + " :: " + key); break; default: throw new ParseException("Unexpected " + JSONParser.getEventString(evt), (int) parser.getPosition()); } evt = parser.nextEvent(); } throw new RuntimeException("unable to parse shape"); } protected Shape readShapeFromCoordinates(String type, JSONParser parser) throws IOException, ParseException { switch(type) { case "Point": return readPoint(parser); case "LineString": return readLineString(parser); case "Circle": return readCircle(parser); case "Polygon": return readPolygon(parser, shapeFactory.polygon()).buildOrRect(); case "MultiPoint": return readMultiPoint(parser); case "MultiLineString": return readMultiLineString(parser); case "MultiPolygon": return readMultiPolygon(parser); default: throw new ParseException("Unable to make shape type: " + type, (int) parser.getPosition()); } } protected ShapeFactory.PolygonBuilder readPolygon(JSONParser parser, ShapeFactory.PolygonBuilder polygonBuilder) throws IOException, ParseException { assert (parser.lastEvent() == JSONParser.ARRAY_START); boolean firstRing = true; int evt = parser.nextEvent(); while (true) { switch (evt) { case JSONParser.ARRAY_START: if (firstRing) { readCoordListXYZ(parser, polygonBuilder); firstRing = false; } else { ShapeFactory.PolygonBuilder.HoleBuilder holeBuilder = polygonBuilder.hole(); readCoordListXYZ(parser, holeBuilder); holeBuilder.endHole(); } break; case JSONParser.ARRAY_END: return polygonBuilder; default: throw new ParseException("Unexpected " + JSONParser.getEventString(evt), (int) parser.getPosition()); } evt = parser.nextEvent(); } } protected Shape readMultiPoint(JSONParser parser) throws IOException, ParseException { assert (parser.lastEvent() == JSONParser.ARRAY_START); ShapeFactory.MultiPointBuilder builder = shapeFactory.multiPoint(); readCoordListXYZ(parser, builder); return builder.build(); } protected Shape readMultiLineString(JSONParser parser) throws IOException, ParseException { assert (parser.lastEvent() == JSONParser.ARRAY_START); // TODO need Spatial4j LineString interface ShapeFactory.MultiLineStringBuilder builder = shapeFactory.multiLineString(); int evt = parser.nextEvent(); while (true) { switch (evt) { case JSONParser.ARRAY_START: ShapeFactory.LineStringBuilder lineStringBuilder = builder.lineString(); readCoordListXYZ(parser, lineStringBuilder); builder.add(lineStringBuilder); break; case JSONParser.ARRAY_END: return builder.build(); default: throw new ParseException("Unexpected " + JSONParser.getEventString(evt), (int) parser.getPosition()); } evt = parser.nextEvent(); } } protected Shape readMultiPolygon(JSONParser parser) throws IOException, ParseException { assert (parser.lastEvent() == JSONParser.ARRAY_START); // TODO need Spatial4j Polygon interface ShapeFactory.MultiPolygonBuilder builder = shapeFactory.multiPolygon(); int evt = parser.nextEvent(); while (true) { switch (evt) { case JSONParser.ARRAY_START: ShapeFactory.PolygonBuilder polygonBuilder = readPolygon(parser, builder.polygon()); builder.add(polygonBuilder); break; case JSONParser.ARRAY_END: return builder.build(); default: throw new ParseException("Unexpected " + JSONParser.getEventString(evt), (int) parser.getPosition()); } evt = parser.nextEvent(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy