/* Copyright 2013 The jeo project. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.jeo.geojson.parser;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import io.jeo.geom.Geom;
import io.jeo.json.parser.ParseException;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.CoordinateSequence;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.geom.impl.PackedCoordinateSequenceFactory;
public class GeometryHandler extends BaseHandler {
GeometryFactory gf = new GeometryFactory();
@Override
public boolean startObject() throws ParseException, IOException {
return true;
}
@Override
public boolean endObject() throws ParseException, IOException {
Geometry g = null;
List geometries = node.consume("geometries", List.class).orElse(null);
if (geometries != null) {
g = createGeometryCollection(geometries);
}
else {
String type = node.consume("type", String.class).orElse(null);
List coordinates = node.consume("coordinates", List.class).orElse(null);
g = createGeometry(type, coordinates);
}
node.setValue(g);
//up();
pop();
return true;
}
@Override
public boolean startObjectEntry(String key) throws ParseException, IOException {
if ("type".equals(key)) {
push(key, new TypeHandler());
}
else if ("coordinates".equals(key)) {
push(key, new CoordinateHandler());
}
else if ("geometries".equals(key)) {
push(key, new GeometryCollectionHandler());
}
return true;
}
@Override
public boolean endObjectEntry() throws ParseException, IOException {
return true;
}
@Override
public boolean primitive(Object value) throws ParseException, IOException {
node.find("type").setValue(value);
return true;
}
Geometry createGeometry(String type, List coordinates) {
switch(Geom.Type.from(type)) {
case POINT:
return createPoint(coordinates);
case LINESTRING:
return createLineString(coordinates);
case POLYGON:
return createPolygon(coordinates);
case MULTIPOINT:
return createMultiPoint(coordinates);
case MULTILINESTRING:
return createMultiLineString(coordinates);
case MULTIPOLYGON:
return createMultiPolygon(coordinates);
default:
throw new IllegalArgumentException("Unexpected geometry type: " + type);
}
}
Point createPoint(List list) {
return gf.createPoint(coord(list));
}
LineString createLineString(List list) {
return gf.createLineString(coordseq(list));
}
Polygon createPolygon(List list) {
LinearRing shell = gf.createLinearRing(coordseq((List)ensureSize(list, 1).get(0)));
LinearRing[] holes = list.size() > 1 ? new LinearRing[list.size()-1] : null;
for (int i = 1; i < list.size(); i++) {
holes[i-1] = gf.createLinearRing(coordseq((List) list.get(i)));
}
return gf.createPolygon(shell, holes);
}
MultiPoint createMultiPoint(List list) {
return gf.createMultiPoint(coordseq(list));
}
MultiLineString createMultiLineString(List list) {
LineString[] lines = new LineString[ensureSize(list, 1).size()];
for (int i = 0; i < list.size(); i++) {
lines[i] = createLineString((List) list.get(i));
}
return gf.createMultiLineString(lines);
}
MultiPolygon createMultiPolygon(List list) {
Polygon[] polys = new Polygon[ensureSize(list, 1).size()];
for (int i = 0; i < list.size(); i++) {
polys[i] = createPolygon((List) list.get(i));
}
return gf.createMultiPolygon(polys);
}
GeometryCollection createGeometryCollection(List geoms) {
return gf.createGeometryCollection((Geometry[])geoms.toArray(new Geometry[geoms.size()]));
}
Coordinate coord(List list) {
ensureSize(list, 2);
double x = number(list.get(0));
double y = number(list.get(1));
double z = list.size() > 2 ? number(list.get(2)) : Double.NaN;
Coordinate c = new Coordinate(x, y);
if (!Double.isNaN(z)) {
c.z = z;
}
return c;
}
CoordinateSequence coordseq(List list) {
ensureSize(list, 1);
int dim = ensureSize((List) list.get(0), 2).size();
CoordinateSequence seq =
PackedCoordinateSequenceFactory.DOUBLE_FACTORY.create(list.size(), dim);
for (int i = 0; i < list.size(); i++) {
List c = (List) list.get(i);
seq.setOrdinate(i, 0, number(c.get(0)));
seq.setOrdinate(i, 1, number(c.get(1)));
if (dim > 2) {
seq.setOrdinate(i, 2, number(c.get(2)));
}
}
return seq;
}
double number(Object obj) {
return ((Number)obj).doubleValue();
}
List ensureSize(List list, int size) {
if (list.size() < size) {
throw new IllegalArgumentException(String.format(Locale.ROOT,
"expected coordinate arary of size %d but is of size %d", size, list.size()));
}
return list;
}
static class GeometryCollectionHandler extends BaseHandler {
@Override
public boolean startArray() throws ParseException, IOException {
return true;
}
@Override
public boolean endArray() throws ParseException, IOException {
List<Geometry> geoms = node.consumeAll("geometry", Geometry.class);
node.setValue(geoms);
pop();
return true;
}
@Override
public boolean startObject() throws ParseException, IOException {
push("geometry", new GeometryHandler());
return true;
}
}
}